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下次大版本改進的時候直接在覈心代碼中處理掉這個問題就完美了。

相關文章
相關標籤/搜索