Принципы обработки ошибок для приложений Node.js + Express.js?

177

Похоже, что сообщения об ошибках / обработка ошибок в приложениях Node.js + Express.js выполняются иначе, чем в других средах. Правильно ли я понимаю, что это работает следующим образом?

А) Обнаруживать ошибки, получая их в качестве параметров для ваших функций обратного вызова. Например:

doSomethingAndRunCallback(function(err) { 
    if(err) {  }
});

B) Сообщите об ошибках в MIDDLEWARE, вызвав next (err). Пример:

handleRequest(req, res, next) {
    // An error occurs…
    next(err);
}

C) Сообщите об ошибках в МАРШРУТАХ, бросая ошибку. Пример:

app.get('/home', function(req, res) {
    // An error occurs
    throw err;
});

D) Обрабатывайте ошибки, настраивая собственный обработчик ошибок с помощью app.error () или используйте общий обработчик ошибок Connect. Пример:

app.error(function(err, req, res, next) {
    console.error(err);
    res.send('Fail Whale, yo.');
});

Являются ли эти четыре принципа основой для обработки ошибок / составления отчетов в приложениях Node.js + Express.js?

Клинт Харрис
источник

Ответы:

183

Обработка ошибок в Node.js обычно имеет формат A). Большинство обратных вызовов возвращают объект ошибки в качестве первого аргумента или null.

Express.js использует промежуточное программное обеспечение, а синтаксис промежуточного программного обеспечения использует B) и E) (упомянуто ниже).

В) это плохая практика, если вы спросите меня.

app.get('/home', function(req, res) {
    // An error occurs
    throw err;
});

Вы можете легко переписать вышесказанное как

app.get('/home', function(req, res, next) {
    // An error occurs
    next(err);
});

Синтаксис промежуточного программного обеспечения действителен в getзапросе.

Что касается D)

(07:26:37 PM) tjholowaychuk: app.error удален в 3.x

TJ только что подтвердил, что app.errorустарел в пользу E

E)

app.use(function(err, req, res, next) {
  // Only handle `next(err)` calls
});

Любое промежуточное программное обеспечение, имеющее длину 4 (4 аргумента), считается промежуточным программным обеспечением ошибки. Когда один звонит, next(err)соединение идет и вызывает основанное на ошибках промежуточное программное обеспечение.

Raynos
источник
11
Спасибо! Для любого, кто может столкнуться с этим в будущем, похоже, что порядок параметров для «метода e» на самом деле err, req, res, next (вместо req, res, next, err).
Клинт Харрис
9
Так что это выглядит великолепно, но проблема, с которой я сталкиваюсь, заключается в том, что некоторые ошибки никогда не попадают в описываемые вами обработчики ошибок и могут быть перехвачены только обработчиком process.on ('uncaughtException', fn). Общепринятое мнение состоит в том, чтобы позволить этому произойти и полагаться на Forever или тому подобное для перезапуска приложения, но если вы сделаете это, как вы вернете дружественную страницу с ошибкой?
Пол
1
@chovy Кроме того, просто к вашему сведению. Обработчик ошибок должен быть передан приложению после брошенной / следующей ошибки. Если это раньше, он не поймает ошибку.
Ли Олайвар
3
следующая (по ошибке) - это, по сути, версия Express,
выдающая
1
@qodeninja Этот метод считается лучшей практикой в ​​Express.
Дэвид Оливерос
11

Люди в Joyent опубликовали действительно проницательный документ о лучших практиках по этому вопросу. Обязательно прочитайте статью для любого разработчика Node.js.

hthserhs
источник
Отличная статья, исправлена ​​ссылка, указывающая на обновленный документ Joyent.
Stephbu
2
статья неплохая: но слишком много текста и мало примеров, это статья для настоящих профессионалов
Герд
3

Почему первый параметр?

Из-за асинхронного характера Node.js, шаблон первый параметр-как- ошибка стал общепризнанным в качестве соглашения для обработки ошибок Node.js в пользовательской среде . Это потому что асинхронный:

try {
    setTimeout(function() {
        throw 'something broke' //Some random error
    }, 5)
}
catch(e) {
   //Will never get caught
}

Таким образом, вместо первого аргумента обратного вызова это единственный разумный способ асинхронной передачи ошибок, а не просто их выдача.

Это приведет к тому, unhandled exceptionчто, в том виде, в каком оно звучит, означает, что ничего не было сделано, чтобы вывести приложение из его запутанного состояния.

Исключения, почему они существуют

Однако стоит отметить, что практически все части Node.js являются источниками событий, а создание исключения является событием низкого уровня, которое может быть обработано, как и все события:

//This won't immediately crash if connection fails
var socket = require("net").createConnection(5000);
socket.on("error", function(err) {
    console.error("calm down...", err)
});

Это можно, но не следует доводить до крайности, чтобы перехватить все ошибки и создать приложение, которое будет изо всех сил стараться никогда не падать. Это ужасная идея почти во всех случаях использования, поскольку она оставляет разработчика без представления о том, что происходит в состоянии приложения, и аналогична упаковке main в try-catch.

Домены - группировка событий логически

В рамках решения этой проблемы исключений, вызывающих сбой приложений, домены позволяют разработчику использовать, например, приложение Express.js, и пытаться разумно закрывать соединения в случае катастрофического сбоя.

ES6

Вероятно, упоминается, что это снова изменится, поскольку ES6 позволяет шаблону генератора создавать асинхронные события, которые по-прежнему можно отследить с помощью блоков try / catch.

Коа (написанный TJ Holowaychuck, тем же автором Express.js) заметно делает это. Он использует yieldоператор ES6 для создания блоков, которые, хотя и выглядят почти синхронно, обрабатываются обычным узлом асинхронно:

app.use(function *(next) {
    try {
        yield next;
    } 
    catch (err) {
        this.status = err.status || 500;
        this.body = err.message;
        this.app.emit('error', err, this);
    }
});

app.use(function *(next) {
    throw new Error('some error');
})

Этот пример был бесстыдно украден отсюда .

Дэвид
источник