從零搭建我的博客網站(二)-全局異常處理

對於異常,咱們能夠分爲 已知異常未知異常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 是空值 而咱們使用了它,致使空指針異常,這類確定是屬於咱們前面說的未知異常。那咱們怎麼區分是已知仍是未知呢?

在服務器接口開發中,一個異常的返回結果,一般包含有:

  • msg:異常信息
  • code:Http 狀態碼
  • errorCode:自定義的異常狀態碼

所以咱們能夠定義 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
}
...
複製代碼

通過測試,咱們能夠拋出那些未知錯誤,到此整個中間件編寫完成!

相關文章
相關標籤/搜索