express全局处理async error

image

问题描述

express 结合 async/await 能够得到很好的开发体验。通常状况下 async/await 在错误处理方面,在最一开始使用Promise时,都习惯用Promise.catch()处理错误,以后async/await 流行后,你们习惯用 try/catch 来处理。git

router.get('/users', async (req, res, next) => {
  try {
   const users = await User.findAll();
    res.send(users);
  } catch (err) {
    logger.error(err.message, err);
    res.send([]);
  }
});

这样作自己没什么问题,可是代码中会存在大量重复的 try/catch,能不能将异常全局捕获统一处理呢?github

假设在没有加try/catch的状况下express

router.get('/users', async (req, res, next) => {
    const users = await User.findAll();
    res.send(users);
});

若是User.findAll()报错了, express全局的错误处理并不能直接捕获 Promise 错误。会报一个 UnhandledPromiseRejectionWarning 错误。json

咱们先来看一下express错误处理的机制。app

express中定义的错误处理中间件函数的定义方式与其余中间件函数基本相同,差异在于错误处理函数有四个自变量而不是三个:(err, req, res, next):async

app.use(function(err, req, res, next) {
  console.error(err.stack);
  res.status(500).send('Something broke!');
});

因此exprees中正常处理与错误处理路径是分开的,多了一个err参数。函数

app.use((err, req, res, next)=>{}) 对应走 Layer.handle_error
app.use((req, res, next)=>{}) 对应走 Layer.handle_request

解决方案

要想express中可以全局捕获`Promise`对象错误,须要再封装一层用来处理Promise对象。ui

const asyncHandler = fn => (req, res, next) =>
  Promise.resolve()
    .then(() => fn(req, res, next))
    .catch(next);

router.get('/users', asyncHandler(async (req, res) => {
  const users = await User.findAll();
  res.send(users);
}));

asyncHandler会将Promise中错误经过catch()捕获并交给 next,这样就会去到 express 全局错误中间件中。spa

但若是在每一个路由请求中都增长这个捕获异常的asyncHandler函数跟在每一个中都加 try/catch本质上没多大区别。并且代码看上去也复杂。翻译

还有一种更简便的方法,使用express-async-errors。原理是:

This is a very minimalistic and unintrusive hack. 
Instead of patching all methods on an express Router, 
it wraps the Layer#handle property in one place, leaving all the rest of the express guts intact.

翻译:这是一种很是简约并且没有入侵性的hack方式。代替了在每一个express路由方法中打补丁的方式,它经过复写express中Layer#handle方法,把每一个Routing Function的错误统一用 next(err) 传递。

用法也很简单,在express后引入require('express-async-errors'),就能够在express错误处理中捕获错误了。

// error handle
app.use((err, req, res, next) => {
  logger.error(err.message, err);
  if (req.xhr) {
    return res.json({
      state: false,
      msg: err.message
    });
  }
  next(err);
});

最后但愿Express下次大版本改进的时候直接在核心代码中处理掉这个问题就完美了。

相关文章
相关标签/搜索