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

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

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

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

B) Сообщить об ошибках в MIDDLEWARE, вызвав следующую (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?

Ответ 1

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

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

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

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

Е)

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

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

Ответ 3

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

Из-за асинхронного характера Node.js шаблон с первым параметром-как-err стал хорошо установленным как соглашение для userland 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)
});

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

Домены - логически группируются события

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

ES6

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

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

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');
})

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