如何处理node.js中的代码异常?

时间:2012-04-30 21:12:51

标签: node.js express

我查看了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)

4 个答案:

答案 0 :(得分:42)

如果您确实希望捕获所有异常并提供除退出Node.js过程之外的一些处理,则需要处理Node的uncaughtException事件。

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

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

这是一个例子。我将这段路由处理代码添加到现有的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:服务器关闭连接而不发送任何数据”消息),因为节点进程已退出,在我调用它的终端中显示stdout的回溯。

答案 1 :(得分:10)

为了能够捕获异步错误,我使用了域。使用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

答案 2 :(得分:2)

您可以使用表达使用的默认错误处理程序,实际上是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);

答案 3 :(得分:1)

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 :(');
});

但是,仅将此作为备用。正确的错误处理仍应在请求处理程序本身具有正确错误状态代码的流行语内进行。