Обработка исключений в экспресс

У меня возникли проблемы с пониманием того, как обращаться с чем-то, что кажется довольно основным аспектом express. Если у меня есть код, который выдает исключение в асинхронном обратном вызове, я не могу поймать это исключение, потому что блок try/catch больше не находится в области к моменту выполнения обратного вызова. В этих сценариях браузер будет висеть, пока он в конечном итоге не откажется от того, что сервер не отвечает. Это очень плохой пользовательский опыт. Я бы предпочел быть возможность немедленно вернуть клиенту ошибку 500. Обработчик ошибок express по умолчанию, по-видимому, не обрабатывает эту ситуацию. Вот пример кода:

var express = require("express");

var app = express();
app.use(app.router);
//express error handler (never called)
app.use(function(err, req, res, next) {
    console.log(err);
    res.send(500);
});

app.get("/test", function(req, res, next) {
    require("fs").readFile("/some/file", function(err, data) {
        a.b(); //blow up
    });
});

app.listen(8888);

в приведенном выше коде строка a.B () создает исключение "ReferenceError: a не определено". Определенный обработчик ошибок никогда не вызывается. Обратите внимание, что объект err, возвращенный fs.readFile () в этом случае имеет значение null, поскольку файл был правильно прочитан. Ошибка-это код внутри асинхронного обработчика.

Я читать этот пост об использовании uncaughtExpception узла даже, но документация говорит не использовать этот метод. Даже если бы я использовал его, как бы я отправил ответ 500 обратно пользователю? Объект express response больше не существует для меня.

Итак, как вы справляетесь с этим сценарием?

2 ответов


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

короткий ответ:: правильно то, что уже происходит: ваша программа должна напечатать трассировку стека и выйти с ошибкой.

базовый мышление:

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

но короче говоря, нет, нет способа получить ссылку на объект HTTP-запроса, который вызвал это исключение, поэтому AFAIK вы не можете изменить то, как это воспринимается конечным пользователем в браузере, вне обработки этого на промежуточном обратном прокси-уровне, где вы можете настроить грубый тайм-аут и отправить более дружественную страницу ошибок (что, конечно, было бы бесполезно для любого запроса, который не является полным HTML-документом).

Библия обработки ошибок в узле

обработка ошибок в узле.js Дэйв Пачеко-это окончательная работа по этой теме, на мой взгляд. Это всесторонний, обширный и тщательный. Я рекомендую периодически читать и перечитывать.


чтобы обратиться к комментариям @asparagino, если необработанное исключение легко воспроизводимо или происходит с высокой частотой, это не крайний случай, это ошибка. Правильная вещь-улучшить свой код, чтобы не генерировать необработанные исключения в этой ситуации. Фактически обработайте условие, таким образом преобразовывая ошибку программиста в рабочую ошибку, и ваша программа может продолжайте без перезапуска и без необработанного исключения.


вы должны использовать промежуточное ПО обработки ошибок express через app.use(error, req, res, next). Express поддерживает отдельный стек промежуточного ПО, который он использует, когда обычный стек промежуточного ПО создает необработанное исключение. (Обратите внимание, что express просто смотрит на arity обратного вызова (количество ожидаемых аргументов), чтобы классифицировать его как обычное промежуточное ПО или промежуточное ПО для обработки ошибок, что немного волшебно, поэтому просто имейте в виду, что вы должны объявить аргументы, как указано выше, чтобы express понял, что это обработка ошибок промежуточное программное обеспечение.)

и основываясь на вашем вопросе и комментариях, просто поймите, что исключения не так уж полезны в узле.js, потому что каждый асинхронный вызов получает новый стек, поэтому обратные вызовы используются везде, и 1-й аргумент является ошибкой универсально. Ваш try/catch блок в Примере только собирается поймать исключение, брошенное непосредственно findById (как если id were undefined, как и в вашем фрагменте), но как только фактический вызов базы данных сделан, это все, вызов стек закончен, и никакие дополнительные исключения не могут произойти, пока не начнется совершенно другой стек вызовов, когда узел вызывает асинхронный обратный вызов IO.

Спасибо за ответ, но это работает только в том случае, если я помещаю try/catch внутри асинхронного обратного вызова и делаю следующий catch(exp). Я хотел бы избежать наличия отдельных блоков try/catch в каждом асинхронном обратном вызове.

нет, это неправда. Вам не нужно вручную вызывать next(exp). Express поймает ошибку и запустите промежуточное программное обеспечение для обработки ошибок (так express делает это дружественные к разработчику страницы отчетов об исключениях в режиме dev). И асинхронные библиотеки не выдают исключений даже при" нормальных " условиях ошибок. Они передают ошибку обратному вызову, поэтому в целом вам не нужно беспокоиться о try/catch, что много в узле. Просто никогда не игнорируйте параметр ошибки, переданный функции обратного вызова, и все в порядке.

вы не видите этот шаблон в узел:

someDb.query(someCriteria, function (error, result) {
  try {
    //some code to deal with result
  } catch (exception) {
    callback(exception);
  }
});

вы видите это, хотя:

someDb.query(someCriteria, function (error, result) {
  if (error) {
    callback(error);
    return;
  }
  //some code to deal with result
});

узел обрабатывает IO по-разному, что означает, что стек вызовов работает по-разному, что означает, что исключения работают по-разному, что означает, что обработка ошибок работает по-разному. Вы можете написать стабильное приложение node / express, которое обрабатывает ошибки без сбоев без написания одной попытки / улова. (у express есть один, который обрабатывает необработанные ошибки, которые пузырятся до самого верха). Это не функциональное ограничение, это просто согласие. асинхронного ввода-вывода, что означает, что вы должны писать свой код по-разному и обрабатывать ошибки с обратными вызовами вместо исключений. Думая о нем, как "ограничение", а не "так оно и есть" вызывает негативный оттенок на то, что это просто техническая реальность. Существуют чистые и надежные шаблоны для обработки исключений как в синхронной, так и в асинхронной парадигме.