Подтвердить что ты не робот

Необработанные отклонения в приложениях 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?

4b9b3361

Ответ 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), если такое обещание отклоняется с ошибкой.