处理快递中的异常

d51*_*512 6 exception node.js express

我无法理解如何处理看起来像是快递的一个非常基本的方面.如果我有一些代码在异步回调中抛出异常,我就无法捕获该异常,因为在回调运行时try/catch块不再在范围内.在这些情况下,浏览器将挂起,直到它最终放弃,表明服务器没有响应.这是一个非常糟糕的用户体验.我宁愿能够立即向客户端返回500错误.默认的快速错误处理程序显然不处理这种情况.以下是一些示例代码:

var express = require("express");

var app = express();
app.use(app.router);
//express error handler (never called)
app.use(function(err, req, res, next) {
    console.log(err);
    res.send(500);
});

app.get("/test", function(req, res, next) {
    require("fs").readFile("/some/file", function(err, data) {
        a.b(); //blow up
    });
});

app.listen(8888);
Run Code Online (Sandbox Code Playgroud)

在上面的代码中,行ab()抛出"ReferenceError:a is not defined"异常.永远不会调用定义的错误处理程序.请注意,fs.readFile()返回的错误对象在这种情况下为null,因为文件已正确读取.错误是异步处理程序中的代码.

我已经读过这篇关于使用node的uncaughtExpception的文章,但文档说不使用该方法.即使我确实使用过它,我如何将500响应发送回用户?我不再使用快速响应对象了.

那么你如何处理这种情况?

Pet*_*ons 7

好的,我只是要发布一个完全不同的答案,因为我的第一个答案有一些有价值的信息,但事后才是主题.

简短的回答:正确的做法是已经发生的事情:您的程序应该打印堆栈跟踪并退出并显示错误.

潜在的想法:

所以我认为你需要考虑不同类别的错误.我的第一个答案是处理数据相关的错误,这是一个编写良好的程序可以并且应该干净利落地处理.你所描述的是一个CRASH.如果您阅读了链接到的node.js文档,那么它是正确的.此程序在此时可以执行的唯一有用的事情是使用堆栈跟踪退出并允许进程主管重新启动它并获得理解状态.一旦程序崩溃,由于极其广泛的错误可能是异常进入堆栈顶部的根本原因,它基本上是不可恢复的.在您的具体示例中,每次都会继续发生此错误,直到修复源代码错误并重新部署应用程序.如果您担心未经测试和错误的代码将进入您的应用程序,添加更多未经测试和错误的错误处理代码并不能解决正确的问题.

但简而言之,不,没有办法获得引起此异常的HTTP请求对象的引用,因此AFAIK无法改变最终用户在浏览器中感知的方式,而不是在中间反向代理层处理此问题您可以在哪里配置原始超时并发送更友好的错误页面(对于任何不是完整HTML文档的请求,这当然是无用的).

节点中的错误处理圣经

在我看来,Dave Pacheco 在Node.js中错误处理是关于这个主题的权威性工作.它是全面,广泛和彻底的.我建议定期阅读和重新阅读.


要解决@asparagino的评论,如果一个未处理的异常很容易重现或高频率发生,那不是一个边缘情况,这是一个错误.正确的做法是改进代码,以便在面对这种情况时不会生成未捕获的异常.实际上处理条件,从而将程序员错误转换为操作错误,并且程序可以继续而无需重新启动且没有未捕获的异常.

  • @PeterLyons - 节点进程通常会处理数十个甚至数千个并发请求.这些中的每一个都可以包含有价值的数据,如果没有完成则会有成本.你正在建立一个变戏法者.一旦它落下一个球,它应该掉下来吗?应记录错误和异常.应检查日志并跟踪和修复错误和边缘条件.这个过程不应该退出. (3认同)

Pet*_*ons 5

您应该使用express的错误处理中间件app.use(error, req, res, next).Express维护一个单独的中间件堆栈,它在正常的中间件堆栈抛出未捕获的异常时使用.(旁注,表达只是查看回调的arity(预期参数的数量)将其归类为常规中间件或错误处理中间件,这有点神奇,所以请记住,您必须声明上述参数以便表达才能理解这是一个错误处理中间件).

根据你的问题和评论,只需要了解异常在node.js中并不是那么有用,因为每个异步调用都会获得一个新的堆栈,这就是回调在任何地方都被使用的原因,第一个参数是一个普遍的错误.您try/catch在本例中块,只是要赶直接引发的异常findById(如果喜欢id被不确定的,因为它是在你的代码段),但一旦到数据库中实际调用时,就是这样,调用堆栈已经结束,没有在节点调用异步IO回调时,在完全不同的调用堆栈启动之前,可能会发生更多异常.

感谢您的回答,但这只有在我将try/catch放在异步回调中并让catch执行next(exp)时才有效.我想避免在每个异步回调中都有单独的try/catch块.

不,那不是真的.您不必手动呼叫next(exp).Express将捕获错误并为您触发错误处理中间件(无论如何,这是开发人员友好的异常报告页面在开发模式中的表达方式).并且即使在"正常"错误条件下,异步库也不会抛出异常.它们将错误传递给回调,因此通常您不必在节点中使用try/catch那么多.永远不要忽略传递给回调函数的错误参数,你没关系.

您在节点中看不到此样板:

someDb.query(someCriteria, function (error, result) {
  try {
    //some code to deal with result
  } catch (exception) {
    callback(exception);
  }
});
Run Code Online (Sandbox Code Playgroud)

你确实看到了这个:

someDb.query(someCriteria, function (error, result) {
  if (error) {
    callback(error);
    return;
  }
  //some code to deal with result
});
Run Code Online (Sandbox Code Playgroud)

节点以不同方式处理IO,这意味着调用堆栈的工作方式不同,这意味着异常的工作方式不同,这意味着错误处 您可以编写一个稳定的节点/快速应用程序来处理错误而不会崩溃,而无需编写单个try/catch.(express有一个处理未被捕获的错误,一直冒泡到顶部).这不是功能限制,它只是异步IO的一个后果,意味着您必须以不同方式编写代码并使用回调而不是异常来处理错误.将其视为一种"限制"而不是"它的方式"正在对一些真正只是技术现实的东西施加负面含义.在同步和异步范例中,异常处理都有干净而强大的模式.