Axios 是目前使用最為廣泛的 http 請求工具包,在進行錯誤處理時,基於框架提供的攔截器,我們可以快速的實現錯誤處理。
本文基於 一種 Laravel 異常上下文解決方案 的評論討論,也歡迎大家訪問我的 部落格。
axios.interceptors.response.use(function (response) {
return response;
}, function (error) {
// 如4xx/5xx等基本錯誤的處理
alert('全域性錯誤處理')
return Promise.reject(error);
});
但 axios 提供的 response 攔截器是全域性的,若我們想對某個具體請求進行錯誤處理時,情況就稍微有點複雜了。
如有一個投票介面,由於後端限制了投票次數,當投票超限時我們需要單獨處理;返回的響應如下:
HTTP/1.0 429 Too Many Requests
{"error_code":4291011,"message":"今日投票次數超限"}
可能對於部分前端同學來說,處理方式是直接在 response 攔截器加上相應的條件判斷就好了:
axios.interceptors.response.use(function (response) {
return response;
}, function (error) {
if (error.response.status == 429) {
if (error.response.data.error_code == 4291011) {
// 單獨處理投票錯誤
} else if (error.response.data.error_code == 4291011) {
// 作品票數異常,需先透過滑動驗證碼
}
}
// 處理其他 如4xx/5xx等基本錯誤的處理
return Promise.reject(error);
});
但是這樣做卻存在很多問題
- 隨著各種錯誤碼的增多,攔截器需要處理的情況越來越多,最終充滿著大量的 if-else
- 具體的錯誤碼應該和具體發起請求的程式碼放在一起,一來方便檢視,二來好擴充套件及定位
所以我們可以把錯誤處理邏輯移到呼叫程式碼處,如下:
axios.post('/vote/1').then(function (response) {
// success
}).catch(function (error) {
let code = error.response.data.error_code
if (code == 4291011) {
alert('投票超限')
} elseif (code == 4030001) {
alert('作品票數異常,需先透過滑動驗證碼')
}
});
但這樣又存在一個問題,由於 axios 攔截器的程式碼會比 catch 先執行,所以當執行到 catch 時,實際上 response 攔截器的程式碼已全部執行完成,所以會先後彈出「全域性錯誤處理」-> 「投票超限」。
這顯然不是我們想要的,我們希望當我們單獨處理錯誤後,先執行具體的業務錯誤處理,最後在執行全域性的錯誤處理。
如下:
- 針對這種沒有 catch 的情況,當請求錯誤後,我們希望由全域性錯誤進行處理。
axios.post('/vote/1').then(function (response) {
// success
})
這預設是可行的,不需要做額外的準備工作。
- 當我們使用 catch 後,我們希望應該先處理自定義的錯誤,最後在處理全域性錯誤。
axios.post('/vote/1').then(function (response) {
// success
}).catch(error => {
// custom error
})
然而目前這樣是行不通的,看了 axios request 方法原始碼後得知,框架在發起請求時,並沒有給我提供相應的鉤子;所以在 Promise 執行到 catch 時,攔截器裡的程式碼一定已經執行過了。
我們只能依賴 axios 提供的 config 來完成這個特性,如下所示:
axios.interceptors.response.use(function (response) {
return response;
}, function (error) {
error.globalErrorProcess = function () {
switch (this.response.status) {
case 401: // 處理基本 401 錯誤
break;
case 404: // 處理基本 404 錯誤
break;
case 403: // 處理基本 403 錯誤
break;
// 處理其他4xx/5xx等基本錯誤的處理
}
return Promise.reject(this);
};
if(error.config.hasOwnProperty('catch') && error.config.catch == true) {
return Promise.reject(error);
}
return error.globalErrorProcess()
});
我們定義一個全域性的錯誤處理器,並把他賦給 error 物件的 globalErrorProcess 方法。接著判斷當前請求 config 是否啟用 catch,若啟用,預設不進行任何錯誤處理,交由呼叫方自行負責;否則用全域性錯誤處理。
在使用時,若需要自定義捕獲錯誤,可顯示傳遞一個 config,相應請求方法的 API 如下:
- axios.request(config)
- axios.get(url[, config])
- axios.delete(url[, config])
- axios.head(url[, config])
- axios.options(url[, config])
- axios.post(url[, data[, config]])
- axios.put(url[, data[, config]])
- axios.patch(url[, data[, config]])
axios.post('https://api.github.com/xxx', null, {catch: true}).then(function (response) {
console.log(response);
}).catch(function (error) {
let code = error.response.data.error_code
if (code == 4291011) {
// 今日投票次數太多,顯示關注公眾號二維碼
} else if (code == 4031011) {
// 不允許的投票時間段,
} else if (code == 4291012) {
// 作品票數異常,需先透過滑動驗證碼
}
return error.globalErrorProcesser()
});
最後別忘了顯示的呼叫全域性錯誤處理,否則是不會懶覺到其他異常處理的。
如果你有更好的方案,歡迎留言一起探討。
參考 & 討論
- Global error handling using axios interceptor · GitHub
- javascript - How to manage axios errors globally or from one point - Stack Overflow
- 一種 Laravel 異常上下文解決方案 | Laravel China 社群
本作品採用《CC 協議》,轉載必須註明作者和本文連結