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
。
1 | app.use(function(err, req, res, next) { |
错误处理函数一般定义在所有路由之后,为了确保Express程序不会因为错误而宕机,则需要把错误抛给这个错误处理函数来处理,在请求内有两种方式把错误抛给错误处理函数。
直接throw new Error()
1 | app.get('/', function (req, res) { |
或者在next()
函数中传入错误对象
1 | app.get('/', function (req, res, next) { |
直接抛出错误,或者在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()
直接跳到下一个中间件;传的值是字符串route
即next('route')
时,直接跳到下一个路由;传入其他值都会跳到错误处理函数。
- next()——跳到下一个中间件
- next(‘route’)——跳到下一个路由
- next(err)——跳到错误处理函数
注意next()
和next('route')
是分别跳到下一个中间件和路由,经常看到说next()
函数会跳到下一个路由的,其实这样说是不准确的,一个路由可以有多个中间件来处理
1 | mw1 = function(req, res, next){} |
例如在mw1
函数中调用了next()
,实际上是跳到mw2
开始执行,而如果我们调用的是next('route')
,那么就会直接跳过mw2
和mw3
,直接开始执行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
对象。