【Bug日记】Express Async异步函数的错误处理

170 阅读1分钟

Bug描述

使用抛出错误的方式来处理异常应该是非常常见的情况,在express中,可以使用四个参数err, req, res, next的中间件来处理错误

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

但是笔者在使用的中,发现这种方式处理不了异步方法中抛出的错误

app.get('/test', async (req, res) => {
    const res = await querySomething()
    if(!res) throw new Error('nothing find')
})

app.use((err, req, res, next) => {
    // 接收不到错误
}) 

// 控制台报错
UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch().

解决方式

笔者尝试在express框架上下手,但是没有找到可以处理异步错误的API(Tips:也有可能是笔者没有发现,如果小伙伴们发现有的话,可以告诉笔者),于是只能通过try catch将异步错误截获,最后再抛出,在全局的错误处理中就能够将其处理

const assert = require('http-assert') // 处理错误的插件

app.use(
    '/api/admin/',
    // token验证
    async (req, res, next) => {
      // 在try语句块中捕获异常
      try {
        assert(req.headers['authorized'], 401, 'token不存在,请先登录')
        const token = req.headers['authorized'].split(' ').pop()
        const payload = jwt.verify(token, app.get('SECRET_KEY'))
        assert(payload, 401, 'token认证失败,请重新登录')
        const obj = await require('../../models/Admin').findById(payload._id)
        assert(obj, 401, '用户认证失败,请重新登录')
        await next()
      } catch (err) {
        next(err) // 在这里重新抛出异常
      }
    }
  )
  
app.use(async (err, req, res, next) => {
    res.send(R.error(err.statusCode || 9999, err.message))
})