【问题标题】:How to handle code exceptions in node.js?如何处理 node.js 中的代码异常?
【发布时间】:2012-05-10 13:32:44
【问题描述】:

我浏览了 Express 的文档,the part describing error handling 对我来说完全不透明。

我认为他们所指的app 是一个实例createServer(),对吗?但是我不知道在处理请求期间发生异常时如何阻止 node.js 炸毁应用程序进程。

我真的不需要任何花哨的东西;每当出现异常时,我只想返回 500 的状态,再加上一个空响应。节点进程不得仅仅因为某处存在未捕获的异常而终止。

有没有一个简单的例子说明如何做到这一点?


var express = require('express');
var http = require('http');

var app = express.createServer();

app.get('/', function(req, res){
    console.log("debug", "calling")
    var options = {
        host: 'www.google.com',
        port: 80,
        path: "/"
    };
    http.get(options, function(response) {
       response.on("data", function(chunk) {
           console.log("data: " + chunk);
           chunk.call(); // no such method; throws here
       });
    }).on('error', function(e) {
       console.log("error connecting" + e.message);
    });
});

app.configure(function(){
    app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
});

app.listen(3000);

使整个应用程序崩溃,产生回溯

mypath/tst.js:16
           chunk.call(); // no such method; throws here
                 ^ TypeError: Object ... has no method 'call'
    at IncomingMessage.<anonymous> (/Library/WebServer/Documents/discovery/tst.js:16:18)
    at IncomingMessage.emit (events.js:67:17)
    at HTTPParser.onBody (http.js:115:23)
    at Socket.ondata (http.js:1150:24)
    at TCP.onread (net.js:374:27)

【问题讨论】:

  • just because there was an uncaught exception somewhere. 如果出现未捕获异常,进程终止。如果你不希望它在异常发生时终止,捕获异常并返回 500 错误。
  • 您可能对非快递方式感兴趣:stackoverflow.com/questions/4213351/…
  • 太好了,非常感谢@Matt!

标签: node.js express


【解决方案1】:

如果你真的想捕获所有异常并提供除了退出 Node.js 进程之外的一些处理,你需要处理 Node 的 uncaughtException 事件。

如果你仔细想想,这是 Node 的事情,而不是 Express 的事情,因为如果你从任意一段代码中抛出异常,就不能保证 Express 能够或将永远看到它,或者处于某个位置把它困住。 (为什么?异常与 Node 风格的异步事件驱动的回调代码交互得不是很好。异常沿着调用堆栈向上移动以找到抛出异常时范围内的 catch() 块。如果 @987654324 @将一些工作推迟到某个事件发生时运行的回调函数,然后返回到事件循环,然后当调用该回调函数时,它直接从主事件循环中调用,并且myFunction不再在调用堆栈上; 如果这个回调函数抛出异常,即使myFunction 有一个try/catch 块,它也不会捕获异常。)

这在实践中意味着,如果您抛出异常并且自己没有捕获它,并且您在 Express 直接调用的函数中这样做,Express 可以捕获异常并调用您安装的错误处理程序,假设您已经配置了一些错误处理中间件,例如app.use(express.errorHandler())。但是,如果您在响应异步事件而调用的函数中抛出相同的异常,Express 将无法捕获它。 (它可以捕获它的唯一方法是侦听全局 Node uncaughtException 事件,这首先是个坏主意,因为它是全局的,您可能需要将它用于其他事情,其次因为 Express 不知道是什么请求与异常相关联。)

这是一个例子。我将这个路由处理代码的 sn-p 添加到现有的 Express 应用程序中:

app.get('/fail/sync', function(req, res) {
   throw new Error('whoops');
});
app.get('/fail/async', function(req, res) {
   process.nextTick(function() {
      throw new Error('whoops');
   });
});

现在,如果我在浏览器中访问 http://localhost:3000/fail/sync,浏览器会转储一个调用堆栈(显示 express.errorHandler 正在运行)。但是,如果我在浏览器中访问 http://localhost:3000/fail/async,浏览器会生气(Chrome 显示“未收到数据:错误 324,net::ERR_EMPTY_RESPONSE:服务器关闭连接但未发送任何数据”消息),因为 Node 进程已退出,在我调用它的终端中的 stdout 上显示回溯。

【讨论】:

  • 这是一个真正出色的答案,因为它不仅阐明 express 如何捕获杂散异常并使用错误中间件处理它们(这是一个意外的默认行为),而且还因为它解释了如何在异步函数中使用 'throw new Error("...")' 不会触发错误中间件,实际上甚至不会被任何包装异步函数的异常处理程序捕获。您在其他任何地方都找不到此信息。
  • 使用domains 是捕获uncaughtException 的更好选择。
  • 域正在被弃用。什么是替代 API?
  • 正在努力抛出异常,Express 没有捕捉到它。很好的解释。
【解决方案2】:

为了能够捕获异步错误,我使用了 domain.使用 Express,您可以尝试以下代码:

function domainWrapper() {
    return function (req, res, next) {
        var reqDomain = domain.create();
        reqDomain.add(req);
        reqDomain.add(res);

        res.on('close', function () {
            reqDomain.dispose();
        });
        reqDomain.on('error', function (err) {
            next(err);            
        });
        reqDomain.run(next)
    }
}
app.use(domainWrapper());
//all your other app.use
app.use(express.errorHandler());

此代码将使您的异步错误被捕获并发送到您的错误处理程序。在此示例中,我使用 express.errorHandler,但它适用于任何处理程序。

有关域名的更多信息:http://nodejs.org/api/domain.html

【讨论】:

【解决方案3】:

您可以使用 express 使用的默认错误处理程序,实际上是connect error handler

var app = require('express').createServer();

app.get('/', function(req, res){
  throw new Error('Error thrown here!');
});

app.configure(function(){
    app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
});

app.listen(3000);

更新 对于您的代码,您实际上需要捕获错误并将其传递给这样的表达

var express = require('express');
var http = require('http');

var app = express.createServer();

app.get('/', function (req, res, next) {
  console.log("debug", "calling");
  var options = {
    host:'www.google.com',
    port:80,
    path:"/"
  };
  http.get(options,
    function (response) {
      response.on("data", function (chunk) {
        try {
          console.log("data: " + chunk);
          chunk.call(); // no such method; throws here

        }
        catch (err) {
          return next(err);
        }
      });
    }).on('error', function (e) {
      console.log("error connecting" + e.message);
    });
});

app.configure(function () {
  app.use(express.errorHandler({ dumpExceptions:true, showStack:true }));
});

app.listen(3000);

【讨论】:

  • 您的示例有效(在添加 var express = require('express'); 之后),但不幸的是,当我在代码中执行相同操作时,它仍然会使进程崩溃。我已经用示例代码更新了问题。
  • 我已经更新了答案,展示了如何捕获错误并将其传递给使用 next() 进行表达。
  • 谢谢@250R!不幸的是,我不能用这样的结构到处乱扔代码逻辑,所以我将使用上面 cmets 中链接到的解决方案 Matt。
  • 看起来最近的 node.js 使用了var errorhandler = require('errorhandler'); app.use(errorhandler());
  • 这个方法增加了很多样板:(
【解决方案4】:

express 5.0.0-alpha.7 came out 27 days ago。有了这个非常具体的预发布版本,您现在终于可以在请求处理程序中拒绝一个 Promise,它会被正确处理:

中间件和处理程序现在可以返回 Promise,如果 Promise 被拒绝,next(err) 将被调用, err 是拒绝的值。 (source)

例如:

app.get('/', async () => {
    throw new Error();
});
app.use((err, req, res, next) => {
    res.status(500).send('unexpected error :(');
});

但是,仅将其用作后备。正确的错误处理仍应发生在请求处理程序本身的捕获短语中,并带有正确的错误状态代码。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-11-11
    • 1970-01-01
    • 2013-11-23
    • 2019-10-30
    • 2014-06-08
    • 2010-10-25
    • 1970-01-01
    相关资源
    最近更新 更多