nodejs異常處理過程/獲取nodejs異常類型/寫一個eggjs異常處理中間件

前言

今天想寫一下eggjs的自定義異常處理中間件,在寫的時候遇到了問題,這個錯誤我捕獲不到類型??前端

處理過程,不喜歡看過程的朋友請直接看解決方法和總結

看一下是什麼:
在這裏插入圖片描述node

拋出的異常是檢驗失敗異常Validation Failed (code: invalid_param)web

我寫了個異常處理中間件,用來處理業務中的異常瀏覽器

module.exports = (options, app) => {

  return async function testMiddleware(ctx, next) {

    try{
      await next();
    }
    catch (err) {
      // 記錄到日誌
      ctx.logger.error(err);
      ctx.throw(err);
    }
  };
};

具體思路是想要根據異常的類型來實現自定義的處理,如驗證失敗就不走onerror。若是不是想要處理的錯誤就ctx.throw(),丟給onerror繼續處理。app

看了下拋出的錯誤類型是:UnprocessableEntityError
在這裏插入圖片描述koa

也沒多想,就用instanceof判斷一下吧:webstorm

module.exports = (options, app) => {

  return async function testMiddleware(ctx, next) {

    try{
      await next();
    }
    catch (err) {
      if (err instanceof UnprocessableEntityError) {    
        // 若是是這個錯誤那麼就本身處理,不然就拋出
        // 記錄到日誌
        ctx.logger.error(err);
        ctx.body = {
          code: err.status,
          err
        }
      } else {
        ctx.throw(err);
      }
    }
  };
};

而後就報了‘UnprocessableEntityError’ undefine錯誤。。async

對的,UnprocessableEntityError我沒有引入,nodejs自帶也沒這個錯誤?那這個錯誤哪來的?函數

接着我順着錯誤去找拋出的源頭,找到了這個:this

在這裏插入圖片描述

this.throw是koa 的throw方法:

在這裏插入圖片描述

在node_modules_koa@2.7.0@koa\lib\context.js的throw函數拋出了這個錯誤,
具體使用的是createError這個函數,再去代碼目錄下找createError:

webstorm ctrl+鼠標左鍵定位跳轉一下,發現原來使用的是http-errors模塊裏面的裏面的createError方法,下面是具體的函數內容:

// node_modules\_http-errors@1.7.3@http-errors\index.js

/**
 * Create a new HTTP Error.
 *
 * @returns {Error}
 * @public
 */

function createError () {
  // so much arity going on ~_~
  var err
  var msg
  var status = 500
  var props = {}
  for (var i = 0; i < arguments.length; i++) {
    var arg = arguments[i]
    if (arg instanceof Error) {     // 若是參數類型就是Error那麼err就是arg
      err = arg
      status = err.status || err.statusCode || status
      continue  // 跳過如下代碼,進入下一輪循環
    }
    switch (typeof arg) {
      case 'string':        // 若是arg是string 那麼err的msg就是arg
        msg = arg
        break
      case 'number':    // 若是是number 那麼錯誤的status就是arg,這裏要求statu是第一個參數
        status = arg
        if (i !== 0) {
          deprecate('non-first-argument status code; replace with createError(' + arg + ', ...)')
        }
        break
      case 'object':    // 若是是類,那麼把類掛載到err上
        props = arg
        break
    }
  }
// 循環完畢了,來判斷一下狀態碼
  if (typeof status === 'number' && (status < 400 || status >= 600)) {
    deprecate('non-error status code; use only 4xx or 5xx status codes')
  }

  if (typeof status !== 'number' ||
    (!statuses[status] && (status < 400 || status >= 600))) {
    status = 500
  }

  // constructor
  var HttpError = createError[status] || createError[codeClass(status)] 
  // 咱們傳的是422,因此返回了UnprocessableEntityError這個構造器
  
  if (!err) {   // 若是不是直接傳入Error
    // create error
    err = HttpError // 若是HttpError爲空,HttpError裏面沒有這個錯誤
      ? new HttpError(msg)  // 執行到這一步,返回一個新的錯誤,
      : new Error(msg || statuses[status])  
    Error.captureStackTrace(err, createError)  // 傳入堆棧信息
  }

  if (!HttpError || !(err instanceof HttpError) || err.status !== status) { //不執行
    // add properties to generic error
    err.expose = status < 500
    err.status = err.statusCode = status
  }

  for (var key in props) {
    if (key !== 'status' && key !== 'statusCode') { //將數據掛載上去
      err[key] = props[key]
    }
  }

  return err
}

大概搞明白了

就是validate在拋出異常以後,調用了koa的throw又調用了http-errors裏面的createError,createError先去找有咩有這個http錯誤,常見的什麼404啊403啊500啊之類的,沒找到就直接把用Error構造,找到了就用對應的錯誤類型構造,這些類型構造又是存在:node_modules/koa/node_modules/http-errors/node_modules/statuses/index.js裏面的,好比上面的422
在這裏插入圖片描述
對應的就是UnprocessableEntityError這個異常。

解決方法

到了這裏,解決就變的很輕鬆了,咱們要捕獲的異常是UnprocessableEntityError,那麼能夠使用err.name來獲取錯誤的名字,獲取到了以後,再根據
在這裏插入圖片描述
validate拋出的固定錯誤碼'Validation Failed'來肯定錯誤。
實際上也能夠直接根據這個錯誤碼來判斷,這個錯誤碼經歷了HttpError的洗禮以後變成了message,能夠經過err.message來獲取。
那麼代碼以下:

module.exports = (options, app) => {

  return async function testMiddleware(ctx, next) {

    try{
      await next();
    }
    catch (err) {
      if (err.message === 'Validation Failed') {
        // 記錄到日誌
        ctx.logger.error(err);
        ctx.body = {
          code: err.status,
          err
        }
      } else {
        ctx.throw(err);
      }
    }
  };
};

再次打開瀏覽器嘗試一下,這個異常被正確的返回給前端了:
在這裏插入圖片描述

總結一下

  1. 錯誤類型,既錯誤名能夠使用err.name獲取
  2. 對應的錯誤消息(相比HttpError更精細的)使用err.message獲取
  3. 錯誤哪怕被本身處理了也仍是寫入日誌,logger.error()一下比較好
  4. 你不想處理的錯誤仍是交給onerror來辦,使用ctx.throw丟給它
  5. 記得處理錯誤要返回信息,既設置ctx.body,否則會返回404的

謝謝觀看,有什麼問題歡迎留言評論,看到儘可能會回覆。。。

相關文章
相關標籤/搜索