Необработанные отклонения в приложениях Express
В моем экспресс-приложении есть много кода на основе ES6, основанного на обещаниях. Если есть ошибка, которая никогда не поймана, я использую следующий код, чтобы справиться с ней:
process.on('unhandledRejection', function(reason, p) {
console.log("Unhandled Rejection:", reason.stack);
process.exit(1);
});
Это отлично подходит для целей отладки.
В производстве, однако, я хотел бы вызвать обработчик ошибок 500, чтобы показать пользователю стандартную страницу "Что-то пошло не так". У меня есть этот catch all обработчик ошибок, который в настоящее время работает для других исключений:
app.use(function(error, req, res, next) {
res.status(500);
res.render('500');
});
Помещение unhandledRejection внутри промежуточного программного обеспечения не работает, так как async и offen приводят к Error: Can't render headers after they are sent to the client.
Как мне сделать рендеринг 500 страниц на unhandledRejection
?
Ответы
Ответ 1
Помещение unhandledRejection внутри промежуточного программного обеспечения... часто приводит к Error: Can't render headers after they are sent to the client.
Сделайте небольшое изменение для обработчика ошибок:
// production error handler
const HTTP_SERVER_ERROR = 500;
app.use(function(err, req, res, next) {
if (res.headersSent) {
return next(err);
}
return res.status(err.status || HTTP_SERVER_ERROR).render('500');
});
Из Документация ExpressJS:
Express поставляется с встроенным обработчиком ошибок, который заботится о любых ошибках, которые могут возникнуть в приложении. Это промежуточное ПО для обработки ошибок по умолчанию добавляется в конец стека промежуточного программного обеспечения.
Если вы передадите ошибку в next(), и вы не будете обрабатывать ее в обработчике ошибок, она будет обработана встроенным обработчиком ошибок - ошибка будет записана клиенту с трассировкой стека. Трассировка стека не включена в производственную среду.
Установите переменную среды NODE_ENV в "production", чтобы запустить приложение в рабочем режиме.
Если вы вызываете next() с ошибкой после того, как вы начали писать ответ, например, если вы столкнулись с ошибкой при потоковой передаче ответа клиенту, Express-обработчик ошибок Express по умолчанию закроет соединение и сделает запрос считанным неудачным.
Поэтому, когда вы добавляете настраиваемый обработчик ошибок, вы захотите делегировать механизмы обработки ошибок по умолчанию в экспресс, когда заголовки уже отправлены клиенту.
Ответ 2
Я использую аргумент next
как обратный вызов catch
(aka errback)
перенаправить любое необработанное отклонение, чтобы выразить обработчик ошибок:
app.get('/foo', function (req, res, next) {
somePromise
.then(function (result) {
res.send(result);
})
.catch(next); // <----- NOTICE!
}
или более короткая форма:
app.get('/foo', function (req, res, next) {
somePromise
.then(function (result) {
res.send(result);
}, next); // <----- NOTICE!
}
и тогда мы могли бы исправить значимый ответ об ошибке
с аргументом err
в обработчике экспресс-ошибок.
например,
app.use(function (err, req, res, /*unused*/ next) {
// bookshelf.js model not found error
if (err.name === 'CustomError' && err.message === 'EmptyResponse') {
return res.status(404).send('Not Found');
}
// ... more error cases...
return res.status(500).send('Unknown Error');
});
IMHO, глобальное событие unhandledRejection
не является окончательным ответом.
например, это подвержено утечке памяти:
app.use(function (req, res, next) {
process.on('unhandledRejection', function(reason, p) {
console.log("Unhandled Rejection:", reason.stack);
res.status(500).send('Unknown Error');
//or next(reason);
});
});
но это тяжелый TOO:
app.use(function (req, res, next) {
var l = process.once('unhandledRejection', function(reason, p) {
console.log("Unhandled Rejection:", reason.stack);
res.status(500).send('Unknown Error');
//next(reason);
});
next();
process.removeEventLister('unhandledRejection', l);
});
IMHO, expressjs нуждается в лучшей поддержке Promise.
Ответ 3
Предполагая, что вы используете Express и некоторый код на основе обещаний, например:
readFile()
.then(readAnotherFile)
.then(doSomethingElse)
.then(...)
Добавьте .catch(next)
в конец вашей цепочки обещаний, а промежуточное ПО Express будет успешно обрабатывать синхронный/асинхронный код с помощью обработчика производственной ошибки.
Вот отличная статья, в которой вы найдете подробное описание: https://strongloop.com/strongblog/async-error-handling-expressjs-es7-promises-generators/
Ответ 4
Я думаю, что express-promise-router был поставлен для решения именно этой проблемы. Он позволяет вашим маршрутам возвращать promises и вызывать next(err)
, если такое обещание отклоняется с ошибкой.