Node.js + Express.js应用程序的错误处理原理?


177

与其他框架相比,Node.js + Express.js应用程序中的错误报告/处理似乎有所不同。我理解它的工作原理是否正确?

A) 通过接收错误作为回调函数的参数来检测错误。例如:

doSomethingAndRunCallback(function(err) { 
    if(err) {  }
});

B) 通过调用next(err)报告 MIDDLEWARE中的​​错误。例:

handleRequest(req, res, next) {
    // An error occurs…
    next(err);
}

C) 通过抛出错误来报告路由中的错误。例:

app.get('/home', function(req, res) {
    // An error occurs
    throw err;
});

D) 通过app.error()配置您自己的错误处理程序来处理错误,或者使用通用的Connect错误处理程序。例:

app.error(function(err, req, res, next) {
    console.error(err);
    res.send('Fail Whale, yo.');
});

这四个原则是否是Node.js + Express.js应用程序中所有错误处理/报告的基础?

Answers:


183

Node.js中的错误处理通常采用A)格式。大多数回调都将错误对象作为第一个参数或返回null

Express.js使用中间件,中间件语法使用B)和E)(如下所述)。

C)如果您问我,这是不好的做法。

app.get('/home', function(req, res) {
    // An error occurs
    throw err;
});

您可以轻松地将上面的内容重写为

app.get('/home', function(req, res, next) {
    // An error occurs
    next(err);
});

中间件语法在get请求中有效。

至于D)

(07:26:37 PM)tjholowaychuk:在3.x中删除了app.error

TJ刚刚确认app.error不赞成使用E

E)

app.use(function(err, req, res, next) {
  // Only handle `next(err)` calls
});

长度为4(4个参数)的任何中间件均被视为错误中间件。当一个呼叫next(err)建立连接并呼叫基于错误的中间件时。


11
谢谢!对于将来可能遇到此问题的任何人,看来“方法e”的参数顺序实际上是err,req,res,next(而不是req,res,next,err)。
克林特·哈里斯

9
因此,这看起来不错,但我看到的一个问题是,某些错误从未进入您描述的错误处理程序,而只能由process.on('uncaughtException',fn)处理程序捕获。传统的想法是让这种情况发生,并依靠Forever等工具重新启动应用程序,但是如果这样做,如何返回友好的错误页面?
保罗

1
@chovy另外,只是一个风俗。在抛出/下一个错误之后,必须将错误处理程序提供给应用程序。如果在此之前,它将不会捕获该错误。
Lee Olayvar

3
next(err)本质上是Express引发错误的版本,尽管如此,您必须在自己的中间件中明确调用它
qodeninja 2014年

1
@qodeninja该方法被认为是Express中的最佳实践。
大卫·奥利弗罗斯


3

为什么要第一个参数?

由于Node.js的异步特性,第一个参数为错误的模式已经很好地确立为用户级Node.js错误处理的约定。这是因为异步:

try {
    setTimeout(function() {
        throw 'something broke' //Some random error
    }, 5)
}
catch(e) {
   //Will never get caught
}

因此,拥有回调的第一个参数几乎是唯一的异步传递错误的明智方法,而不仅仅是抛出错误。

这样做将导致unhandled exception声音听起来像暗示着没有采取任何措施使应用程序脱离混乱状态。

例外,为什么存在

但是,值得注意的是,Node.js的几乎所有部分都是事件发射器,并且引发异常是一个低级事件,可以像处理所有事件一样处理:

//This won't immediately crash if connection fails
var socket = require("net").createConnection(5000);
socket.on("error", function(err) {
    console.error("calm down...", err)
});

不能但不能采取极端措施来捕获所有错误并使应用程序竭尽全力以防崩溃。在几乎每个用例中,这都是一个糟糕的主意,因为它会使开发人员对应用程序状态中发生的事情一无所知,并且类似于将主体包装在try-catch中。

域-逻辑分组事件

作为处理使应用程序崩溃的异常问题的一部分,允许开发人员使用Express.js应用程序,并在发生灾难性故障时合理地尝试关闭连接。

ES6

可能要提到的是,这将再次发生变化,因为ES6允许生成器模式创建异步事件,这些事件仍然可以使用try / catch块捕获。

Koa(由Express.js的原作者TJ Holowaychuck编写)明显地做到了这一点。它使用ES6 yield语句创建块,这些块虽然看起来几乎是同步的,但是是以通常的节点异步方式处理的:

app.use(function *(next) {
    try {
        yield next;
    } 
    catch (err) {
        this.status = err.status || 500;
        this.body = err.message;
        this.app.emit('error', err, this);
    }
});

app.use(function *(next) {
    throw new Error('some error');
})

这个例子是从这里来的

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.