Express框架中的错误处理 - 阿飞的自留地

Express框架中的错误处理

Whichever method you use, if you want Express error handlers to be called in and the application to survive, you must ensure that Express receives the error.

Express官方文档中提到,要确保你的Express程序在遇到错误的时候不会宕机,就得确保Express能接收到程序抛出的错误。

uncaughtException导致的node宕机

如上所说,Express程序必须手动处理所有可能抛出的错误才不会发生宕机,或者说降低宕机的风险,这和node是息息相关的,毕竟Express是基于Nodejs的,在node中,在遇到uncaughtException时会直接结束node进程,之前写过一篇文章

Promise的UnhandledPromiseRejectionWarning问题

就是提到的node的这个特性,对Promise对象没有用.catch()来捕捉错误就会使node可能宕机。

当然了我们可以用pm2或者forever之类的node进程管理工具来管理重启和记录错误,这样也更利于我们之后对程序进行修复。

但是,在运行过程中,我们还是更希望我们的程序宕机的次数越少越好,如果我们的程序的Session是存储在内存中的,那么程序宕机重启之后,所有的用户登录状态也将不复存在。

Error Handler 错误处理函数

Express框架内置了一个错误处理函数(Error Handler),这个函数是以中间件(MiddleWare)的形式来使用的,只不过这个中间件和其他的中间件稍微不一样,那就是错误处理函数接受4个参数,比常规中间件多了接收错误的参数err

app.use(function(err, req, res, next) {
  // 处理错误
});

错误处理函数一般定义在所有路由之后,为了确保Express程序不会因为错误而宕机,则需要把错误抛给这个错误处理函数来处理,在请求内有两种方式把错误抛给错误处理函数。

直接throw new Error()

app.get('/', function (req, res) {
  throw new Error('BROKEN') // Express will catch this on its own.
})

或者在next()函数中传入错误对象

app.get('/', function (req, res, next) {
  fs.readFile('/file-does-not-exist', function (err, data) {
    if (err) {
      next(err) // Pass errors to Express.
    } else {
      res.send(data)
    }
  })
})

直接抛出错误,或者在next()函数中传入错误对象都能直接跳过剩余的路由,直接进入错误处理函数。

参考官方文档

If you pass anything to the next() function (except the string 'route'), Express regards the current request as being an error and will skip any remaining non-error handling routing and middleware functions.

next()函数

上面的官方文档说道 except the string 'route'

意思即是next()直接跳到下一个中间件;传的值是字符串routenext('route')时,直接跳到下一个路由;传入其他值都会跳到错误处理函数。

  • next()——跳到下一个中间件
  • next(‘route’)——跳到下一个路由
  • next(err)——跳到错误处理函数

注意next()next('route')是分别跳到下一个中间件路由,经常看到说next()函数会跳到下一个路由的,其实这样说是不准确的,一个路由可以有多个中间件来处理

mw1 = function(req, res, next){}
mw2 = function(req, res, next){}
mw3 = function(req, res, next){}
mw4 = function(req, res, next){}

app.get('/', mw1, mw2, mw3)
app.get('/', mw4)

例如在mw1函数中调用了next(),实际上是跳到mw2开始执行,而如果我们调用的是next('route'),那么就会直接跳过mw2mw3,直接开始执行mw4

之所以有时候混淆是因为经常一个路由我们只用一个函数来处理,而不是链式处理,一个路由一个函数处理,调用next()和调用next('route')的效果是一样的。

值得一提的还有,在官方文档中还提到

Starting with Express 5, route handlers and middleware that return a Promise will call next(value) automatically when they reject or throw an error.

也就是说,从Express5开始,在请求中或者中间件中的Promise对象,即使没有手动调用next(err)throw new Error(),如果Promise中抛出了错误或者reject了,那么Express会自动调用next(err),其中的err为Promise内抛出的错误或者reject的值,如果不存在就传入一个默认的Error对象。

官方文档中关于Error Handler的直达地址:
[Express error handling](https://www.expressjs.com.cn/en/guide/error-handling.html

添加新评论

电子邮件地址不会被公开,评论内容可能需要管理员审核后显示。