【问题标题】:Unhandled rejections in Express applicationsExpress 应用程序中未处理的拒绝
【发布时间】:2016-01-29 08:25:23
【问题描述】:

我有很多基于 ES6 Promise 的代码在我的 express 应用中运行。如果有一个从未被捕获的错误,我将使用以下代码来处理它:

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 放在中间件中不起作用,因为它是异步的并且会导致 Error: Can't render headers after they are sent to the client.

我将如何在unhandledRejection 上呈现 500 页面?

【问题讨论】:

    标签: node.js express es6-promise


    【解决方案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 Documentation

    Express 带有一个内置的错误处理程序,可以处理应用程序中可能遇到的任何错误。这个默认的错误处理中间件被添加到中间件堆栈的末尾。

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

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

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

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

    【讨论】:

    • 嗨。请添加更多代码。我不知道如何在快递应用程序中使用此代码process.on('unhandledRejection', error => { /* what should i do here */ }); 处理 unhandledRejection
    【解决方案2】:

    我使用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!
    }
    

    然后我们可以发出有意义的错误响应 在 express 错误处理程序中使用 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);
      });
    });
    

    但这重了:

    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 提供更好的支持。

    【讨论】:

    • 所以我们应该始终使用next 来处理错误,并且没有用于承诺拒绝的全局错误处理程序:( 这太糟糕了!
    • 如果可以添加一次监听器,为什么还要移除unhandledRejection事件监听器?
    • 根据文档Adds a one-time listener function for the event named eventName. The next time eventName is triggered, this listener is removed and then invoked. - nodejs.org/api/events.html#events_event_removelistener
    • @iolo 你提到了Too heavy,你能解释一下吗?
    • @loretoparisi 那个文档参考是错误的和误导性的。
    【解决方案3】:

    express-promise-router 正是为了解决这个问题。它允许你的路由返回 Promise,如果这样的 Promise 因错误而被拒绝,它将调用 next(err)

    【讨论】:

    • 我认为现在这确实是要走的路;与尝试在整个代码中散布错误捕获逻辑相比,它要记住的要少得多,而且要干净得多。
    【解决方案4】:

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

    创建库express-async-errors 就是为了解决这些错误。

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

    【讨论】:

    • 不错!有了那个库,这很简单。我鼓励构建自己的错误处理程序中间件并尝试捕获所有异步调用并在 catchs 中调用 next(err) 但如果你想节省时间,这个库似乎工作得很好!
    【解决方案5】:

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

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

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

    这是一篇深入了解您正在寻找的内容的精彩文章:https://strongloop.com/strongblog/async-error-handling-expressjs-es7-promises-generators/

    【讨论】:

      【解决方案6】:

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

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-07-21
        • 2017-11-28
        • 1970-01-01
        • 2019-10-13
        相关资源
        最近更新 更多