如何處理Express異常?

譯者按:根據墨菲定律:「有可能出錯的事情,就會出錯」。那麼,既然代碼必然會出錯,咱們就應該處理好異常。javascript

爲了保證可讀性,本文采用意譯而非直譯。另外,本文版權歸原做者全部,翻譯僅用於學習。前端

處理異常是編程很是重要的一點。咱們的程序依賴於第三方服務、數據庫以及咱們的用戶,一切都不可預料。數據庫可能會宕機,第三方服務可能會崩潰,用戶可能會使用錯誤的參數調用咱們的接口。java

爲了處理各類複雜的狀況,咱們必須處理好代碼異常,下面是代碼示例:數據庫

app.get('/users/:id', (req, res) => {
  const userId = req.params.id
  if (!userId) {
    return res.sendStatus(400).json({
      error: 'Missing id'
    })
  }

  Users.get(userId, (err, user) => {
    if (err) {
      return res.sendStatus(500).json(err)
    }

    res.send(users)
  })
})

代碼中處理了異常,可是存在問題:express

  • 在多處代碼處理異常
  • 沒有使用Express的異常處理模塊來統一處理異常

接下來,咱們來一步步優化代碼異常處理。npm

Express異常處理中間件

全部Express的路由處理函數都有第三個參數next,它能夠用來調用下一個中間件,也能夠將錯誤傳遞給錯誤處理中間件:編程

app.get('/users/:id', (req, res, next) => {
  const userId = req.params.id
  if (!userId) {
    const error = new Error('missing id')
    error.httpStatusCode = 400
    return next(error)
  }

  Users.get(userId, (err, user) => {
    if (err) {
      err.httpStatusCode = 500
      return next(err)
    }

    res.send(users)
  })
})

使用next(err),Express就知道出錯了,並把這個錯誤傳遞給錯誤處理模塊。爲了處理這些錯誤,須要添加一箇中間件,它有4個參數:json

app.use((err, req, res, next) => {
  // log the error...
  res.sendStatus(err.httpStatusCode).json(err)
})

這樣,咱們就能夠使用中間件統一處理錯誤了。可是,如今的代碼有些重複:建立錯誤,指定HTTP狀態碼,使用next(err)...後端

Fundebug是全棧JavaScript錯誤監控平臺,支持各類前端和後端框架,能夠幫助您第一時間發現BUG!app

boom

boom是一個兼容HTTP的錯誤對象,他提供了一些標準的HTTP錯誤,好比400(參數錯誤)等。

const boom = require('boom')
app.get('/users/:id', (req, res, next) => {
  const userId = req.params.id
  if (!userId) {
    return next(boom.badRequest('missing id'))
  }

  Users.get(userId, (err, user) => {
    if (err) {
      return next(boom.badImplementation(err))
    }

    res.send(users)
  })
})

錯誤處理中間件須要稍做修改:

app.use((err, req, res, next) => {
  if (err.isServer) {
    // log the error...
    // probably you don't want to log unauthorized access
    // or do you?
  }
  return res.status(err.output.statusCode).json(err.output.payload);
})

Async/Await錯誤處理

使用Async/Await以後,能夠這樣處理Express異常:

  • 將中間件使用Promise封裝起來,使用catch統一處理異常
  • 在中間件中,直接拋出異常就能夠了
const boom = require('boom');
// wrapper for our async route handlers
// probably you want to move it to a new file
const asyncMiddleware = fn => (req, res, next) => {
  Promise.resolve(fn(req, res, next)).catch((err) => {
    if (!err.isBoom) {
      return next(boom.badImplementation(err));
    }
    next(err);
  });
};
// the async route handler
app.get('/users/:id', asyncMiddleware(async (req, res) => {
  const userId = req.params.id
  if (!userId) {
    throw boom.badRequest('missing id')
  }

  const user = await Users.get(userId)
  res.json(user)
}))

參考

  • 驗證HTTP請求參數能夠使用joi模塊
  • 打印日誌能夠使用winston或者pino模塊

clipboard.png

版權聲明:
轉載時請註明做者Fundebug以及本文地址:
https://blog.fundebug.com/2017/12/06/handle-express-error/

相關文章
相關標籤/搜索