Express应用程序中未处理的拒绝

时间:2015-10-29 09:17:19

标签: node.js express es6-promise

我在Express应用程序中运行了很多基于ES6 promise的代码。如果有错误从未被捕获,我可以使用以下代码来处理它:

process.on('unhandledRejection', function(reason, p) {
  console.log("Unhandled Rejection:", reason.stack);
  process.exit(1);
});

这适用于调试目的。

在生产中,我想触发500错误处理程序,向用户显示标准"出错了"页。我有这个捕获当前适用于其他异常的所有错误处理程序:

app.use(function(error, req, res, next) {
  res.status(500);
  res.render('500');
});

将unhandledRejection放在中间件中不起作用,因为它的异步和offen导致Error: Can't render headers after they are sent to the client.

如何在unhandledRejection上呈现500页?

6 个答案:

答案 0 :(得分:24)

  

将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 Documentation

  

Express附带内置错误处理程序,可处理应用程序中可能遇到的任何错误。此缺省错误处理中间件将添加到中间件堆栈的末尾。

     

如果将错误传递给next()并且您没有在错误处理程序中处理它,则它将由内置错误处理程序处理 - 错误将通过堆栈跟踪写入客户端。堆栈跟踪不包含在生产环境中。

     
    

将环境变量NODE_ENV设置为“production”,以便在生产模式下运行应用程序。

  
     

如果在开始编写响应后调用next()并出现错误,例如,如果在将响应流式传输到客户端时遇到错误,则Express的默认错误处理程序将关闭连接并考虑请求失败。

     

因此,当您添加自定义错误处理程序时,您将希望在已将标题发送到客户端时委派给express中的默认错误处理机制。

答案 1 :(得分:19)

我使用next参数作为catch回调(又名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');
});

恕我直言,全球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);
});
恕我直言,expressjs需要更好的支持Promise。

答案 2 :(得分:11)

express-promise-router是为了解决这个问题。它允许您的路由返回承诺,并且如果此类承诺被拒绝且错误,则会调用next(err)

答案 3 :(得分:3)

假设您正在使用Express和一些基于承诺的代码,如下所示:

readFile() .then(readAnotherFile) .then(doSomethingElse) .then(...)

在您的保证链的末尾添加.catch(next),Express的中间件将使用您的生产错误处理程序成功处理同步/异步代码。

这是一篇很棒的文章,深入探讨了您的目标:https://strongloop.com/strongblog/async-error-handling-expressjs-es7-promises-generators/

答案 4 :(得分:2)

默认情况下,如果您的请求是引发错误的async函数,则express不会将错误传递给中间件。 而是调用process.on('unhandledRejection', callback),请求将被阻止。

创建了express-async-errors库来解决这些错误。

您需要将require('express-async-errors');添加到代码中,该库将确保所有函数都到达处理程序。 即使是未处理的拒绝。

答案 5 :(得分:0)

我一直在寻找一种干净的方式来处理它,Express 5 现在通过设计处理异步承诺:

<块引用>

从 Express 5 开始,路由处理程序和中间件返回一个 Promise 在拒绝或抛出时会自动调用 next(value) 一个错误。例如

https://expressjs.com/en/guide/error-handling.html