對於異常,咱們能夠分爲 已知異常 和 未知異常node
已知異常就是程序中可以知道的異常,如:客戶端參數傳遞錯誤,服務端拋出異常、如客戶端無權限訪問等等這類,這類錯誤就是已知異常。git
未知異常就是程序中不能預想的錯誤,最多見的服務器程序拋出狀態碼 500 的異常。又好比咱們單詞拼寫錯誤,致使程序沒法運行等等,這種就是未知異常。github
咱們在中間件拋出一個異常看看json
app.use((ctx, next) => {
throw Error('error');
ctx.msg += 'world';
next();
});
複製代碼
咱們在控制檯獲得 UnhandledPromiseRejectionWarning Error:error 錯誤服務器
咱們如今加上 **app.onerror ** 來攔截這個錯誤app
app.use((ctx, next) => {
throw Error('error');
ctx.msg += 'world';
next();
});
app.use(ctx => {
ctx.body = 'hello word';
});
app.onerror = (err) => {
console.log('捕獲到了!', err.message);
}
複製代碼
再次運行,發現 onerror 竟然沒有攔截到koa
查閱官網中記載的錯誤處理方法 Error Handlingasync
其實吧,咱們違反了 koa 的設計,有兩種方法處理這個問題。函數
若是不想改爲 async 函數,那就在全部 next() 前面加上 return 便可。測試
若是是 async 函數,那全部 next 前面加 await 便可
知道了這個咱們開始編寫全局異常中間件
編寫捕捉異常處理中間件 catchError.js
const catchError = async (ctx, next) => {
try {
await next()
} catch (error) {
}
}
module.exports = catchError
複製代碼
在 app.js 加載中間件
全局異常中間件監聽、處理,所以放在全部中間件的最前面
const catchError = require('./middlewares/exception')
const app = new Koa()
app.use(catchError)
...
app.listen(8000)
複製代碼
咱們來測試下可否正確攔截到異常
const Koa = require('koa');
const app = new Koa();
const catchError = async (ctx, next) => {
try {
await next()
} catch (error) {
console.log(error);
}
}
app.use(catchError)
app.use((ctx, next) => {
console.log(a);
});
app.listen(8000);
複製代碼
咱們運行發現 控制檯成功攔截錯誤 ReferenceError: a is not defined
向上面的 a 是空值 而咱們使用了它,致使空指針異常,這類確定是屬於咱們前面說的未知異常。那咱們怎麼區分是已知仍是未知呢?
在服務器接口開發中,一個異常的返回結果,一般包含有:
所以咱們能夠定義 HttpException 只要是出現這異常屬於HttpException都屬於已知異常。
// 定義HttpException繼承Error這個類
class HttpException extends Error {
constructor(msg = '服務器異常', errorCode = 500, code = 400) {
super()
/** * 錯誤信息 */
this.msg = msg
/** * Http 狀態碼 */
this.code = code
/** * 自定義的異常狀態碼 */
this.errorCode = errorCode
}
}
複製代碼
咱們如今改寫下中間件那裏,區分是已知異常仍是未知異常
const { HttpException } = require("../core/httpException")
const catchError = async (ctx, next) => {
try {
await next()
} catch (error) {
const isHttpException = error instanceof HttpException
//判斷是不是已知錯誤
if (isHttpException) {
ctx.body = {
msg: error.msg,
errorCode: error.errorCode,
request: `${ctx.method} ${ctx.path}`
}
ctx.status = error.code
} else {
ctx.body = {
msg: '服務器出現了未知異常',
errorCode: 999,
request: `${ctx.method} ${ctx.path}`
}
ctx.status = 500
}
}
}
複製代碼
Ok,咱們如今在 app.js 測試下
咱們啓動服務 訪問 http://localhost:8000/ 發現拋出了 {"msg":"服務器出現了未知異常","errorCode":999,"request":"GET /"}
說明咱們自定義的已知錯誤被攔截到了.
有了上面的 異常的基類,咱們很容易定義一些常見的異常,如:參數校驗失敗
class ParameterExceptio extends HttpException {
constructor(msg, errorCode) {
super()
this.code = 400;
this.msg = msg || '參數錯誤';
this.errorCode = errorCode || 400;
}
}
複製代碼
成功返回
class Success extends HttpException {
constructor(msg, errorCode) {
super()
this.code = 200;
this.msg = msg || '成功';
this.errorCode = errorCode || 200;
}
}
複製代碼
權限認證
class AuthFaild extends HttpException {
constructor() {
super()
this.code = 401;
this.msg = '認證失敗';
this.errorCode = 1004;
}
}
複製代碼
你們能夠根據本身業務定義你們須要的異常狀況
前面咱們在全局異常中間件那裏區分了 已知異常異常 和 未知異常。可是返回了同樣的錯誤,這對咱們開發環境 調試是不友好的。咱們須要在控制檯拋出異常,方便咱們定位問題
改寫 pack.json 啓動命名
"scripts": {
"start": "set NODE_ENV=dev&& nodemon --inspect app.js"
}
複製代碼
咱們啓動服務後告訴當前是開發環境,前面已經說了,咱們只須要在開發環境中拋出未知錯誤,以下:
const isHttpException = error instanceof HttpException
// 開發環境下輸出 異常 error
if (process.env.NODE_ENV === 'dev' && !isHttpException) {
throw error
}
...
複製代碼
通過測試,咱們能夠拋出那些未知錯誤,到此整個中間件編寫完成!