今天想寫一下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); } } }; };
再次打開瀏覽器嘗試一下,這個異常被正確的返回給前端了: