Eaxios 是基於 axios 封裝的網絡請求庫,在保持 API 與 axios 基本一致的狀況下,簡化服務端響應內容和各類異常狀況的處理。前端
如上圖所示,是一次 Ajax 請求可能輸出的結果,在前端咱們須要根據輸出結果給用戶不一樣的提示。ios
可是,現有的 Axios 庫對於異常結果沒有提供較好的封裝,Axios Promise catch 裏包含各類類型的錯誤,並且沒有提供錯誤碼來識別請求失敗的緣由。並且不少服務端接口會返回本身的錯誤碼,這樣在 Axios Promise then 裏也須要處理業務異常。git
此外,Axios 自己以下所述的一些問題和侷限性。github
對於 500 等錯誤,響應內容會丟失,因此不要去配置 responseType 爲 json,對於使用者來講容易採到這個坑。web
ps:雖然 Axios 官方文檔聲明 responseType 是 json,實際上底層調用 XMLHttpRequest 的 responseType 是沒有傳值的,應該是爲了規避這個問題。npm
ps:應該是爲了規避上一個問題,默認提供了一個響應處理函數進行 JSON 解析,可是這會影響性能(500 等響應內容值較多時,會形成頁面卡頓)。雖然 transformResponse 能夠轉換 response,實際接收到的參數是 response.data,因此沒法判斷具體狀況來決定是否進行解析 JSON。json
理想狀況下,使用者但願 then 返回有效的數據,catch 返回各類錯誤狀況:請求被取消、網絡異常、網絡超時、服務端異常、服務端數據格式錯誤、業務異常。axios
content-type
爲 application/x-www-form-urlencoded
類型的請求體,使用起來不夠方便優化方案:api
經過請求攔截器實現不給 Axios 傳遞 transformResponse 配置,且將配置備份到其餘字段上,而後在響應攔截器中將響應對象 response 傳遞給 transformResponse 處理。響應攔截器根據 response 提供的狀態碼、響應頭和響應內容判斷是否要進行 JSON 轉換。服務器
eaxios 主要對響應的處理作了一些優化,除了如下部分,eaxios 的 api 與 axios 保持一致:
eaxios 請求配置的 transformResponse 傳參和處理時機發生了變化
axios 在服務端響應內容後就會調用 transformResponse 進行響應轉換,eaxios 響應後內部自動根據響應頭和 responseType 進行 JSON 解析,而後將解析後的數據和 response 傳給 transformResponse,transformResponse 返回的數據最終會被 Promise resovle 給外部調用者。
假設服務端返回的數據結構爲 { code: 0, message: 'success', data: {} }
,code 爲 0 表示正確的響應,非 0 表示異常,接口請求的代碼示例以下所示:
const eaxios = require('eaxios'); eaxios.defaults.transformResponse = [ function (data, response) { if (typeof data === 'object') { // 默認約定有成功解析 JSON 對象,就認爲服務端成功響應,且有提供錯誤碼 if (data.code === 0) { return data.data; } else { throw eaxios.createError(data.message, data.code, response); } } else { // 50x 等服務異常狀況 throw eaxios.createError( data, response.config.responseError.SERVER_ERROR, response ); } }, ]; return eaxios('https://run.mocky.io/v3/4f503449-0349-467e-a38a-c804956712b7') .then((data) => { console.log('success', data.id); }) .catch((error) => { console.log('failure', error.code); // UNKNOWN、REQUEST_OFFLINE、REQUEST_TIMEOUT、SERVER_ERROR、RESPONSE_INVALID 和業務錯誤碼 });
ps:若是存在服務單接口請求規範,能夠經過 eaxios.create 建立適用於不一樣接口規範的請求函數。
eaxios 的請求處理函數 then 只會接收到 transformResponse 轉換後的數據,對於網絡、超時、服務端異常和業務異常等問題,會在 catch 接收一個 EaxiosError 類型的錯誤對象。
interface EaxiosError<T = any> extends Error { config: EaxiosRequestConfig; code?: string; request?: any; response?: EaxiosResponse<T>; isAxiosError: boolean; toJSON: () => object; }
錯誤處理函數能夠根據錯誤碼 code 來處理異常,code 可能的值爲 UNKNOWN、REQUEST_OFFLINE、REQUEST_TIMEOUT、SERVER_ERROR、RESPONSE_INVALID 和其餘業務錯誤碼。
ps:若是要定製錯誤碼,能夠在請求配置中添加配置項 responseError
。
eaxios.defaults.responseError = { REQUEST_OFFLINE: '1'REQUEST_OFFLINE };
下面以 { code: 0, message: 'success', data: { } }
這樣的接口規範爲例,演示如何使用 eaxios。
const eaxios = require('eaxios'); const request = eaxios.create({ baseURL: 'https://run.mocky.io/v3', timeout: 30000, transformResponse: [ function (data, response) { if (typeof data === 'object') { if (data.code === 0) { return data.data; } else { throw eaxios.createError(data.message, data.code, response); } } else { throw eaxios.createError( data, response.config.responseError.SERVER_ERROR, response, ); } }, ], }); request.interceptors.response.use( function (response) { return response; }, function (error) { if (error && error.code) { if (error.code === 'UNKNOWN') { console.log('未知錯誤'); } else if (error.code === 'REQUEST_OFFLINE') { console.log('網絡未鏈接'); } else if (error.code === 'REQUEST_TIMEOUT') { console.log('網絡有點慢,請求超時了'); } else if (error.code === 'SERVER_ERROR') { console.log('系統出問題了'); } else if (error.code === 'RESPONSE_INVALID') { console.log('服務端 bug'); } else if (error.code === '10000') { // 假設 10000 爲登陸會話過時 console.log('登陸會話失效'); } else { console.log('根據狀況是否要消息提示,仍是外部處理') } } throw error; }, ); function printError(error) { console.log( `code: ${error.code}, name: ${error.name}, message: ${error.message}, isAxiosError: ${error.isAxiosError}, stack:\n${error.stack}`, ); } function success() { console.log('>> success'); return request('/4f503449-0349-467e-a38a-c804956712b7') .then((data) => { console.log('success', data); }) .catch((error) => { printError(error); }); } function failure() { console.log('>> failure'); return request('/42d7c21d-5ae6-4b52-9c2d-4c3dd221eba4') .then((data) => { console.log('success', data); }) .catch((error) => { printError(error); }); } function invalid() { console.log('>> invalid'); return request('/1b23549f-c918-4362-9ac8-35bc275c09f0') .then((data) => { console.log('success', data); }) .catch((error) => { printError(error); }); } function server_500() { console.log('>> server_500'); return request('/2a9d8c00-9688-4d36-b2de-2dee5e81f5b3') .then((data) => { console.log('success', data); }) .catch((error) => { printError(error); }); } success().then(failure).then(invalid).then(server_500); /* log >> success success { id: 1 } >> failure 登陸會話失效 code: 10000, name: Error, message: error, isAxiosError: true, stack: ... >> invalid 服務端 bug code: RESPONSE_INVALID, name: SyntaxError, message: Unexpected token V in JSON at position 0, isAxiosError: true, stack: ... >> server_500 系統出問題了 code: SERVER_ERROR, name: Error, message: ..., stack: ... */
eaxios 依賴 URLSearchParams 處理表單類型的請求參數,不支持的環境須要引入響應的 polyfill
require("core-js/modules/web.url-search-params.js")