前端學習 vben 之 axios interceptors
interceptor
攔截器,是一種軟件設計模式,核心思想就是在程序執行的特定階段(如請求發送前,響應返回后,方法調用前后等)自動插入自定義邏輯。實現對核心流程的“攔截”和增強。它本質上是一種面向切面編程(AOP) 的具體實現,用于解耦橫跨多個模塊的通用功能(如日志、認證、錯誤處理)。
axios 中的 interceptor
axios 中實現了 interceptor,可以在發送請求前攔截請求,對請求進行一些處理,比如添加 token,添加請求頭等。
在響應返回之后,也可以對響應進行一些處理,比如將響應數據轉換成 json 對象,或者處理錯誤等。
vben 中的 axios 的 interceptor
vben 中預設了三個攔截器,文件地址packages\effects\request\src\request-client\preset-interceptors.ts
請求攔截器
請求攔截器,只寫了一些設置請求頭 token 的邏輯,和客戶端所偏好的自然語言。
config => {const accessStore = useAccessStore();config.headers.Authorization = formatToken(accessStore.accessToken);config.headers["Accept-Language"] = preferences.app.locale;return config;
};
響應攔截器
defaultResponseInterceptor
首先,我們要知道什么時候會觸發 axios 請求成功的響應攔截器。
// `validateStatus` 定義了對于給定的 HTTP狀態碼是 resolve 還是 reject promise。// 如果 `validateStatus` 返回 `true` (或者設置為 `null` 或 `undefined`),// 則promise 將會 resolved,否則是 rejected。validateStatus: function (status) {return status >= 200 && status < 300; // 默認值},
可知,當 http status 為 200-300 之間時,才會觸發請求響應的成功攔截器。
讓我們看一下 axios 響應數據的格式
{// `data` 由服務器提供的響應"data": {},// `status` 來自服務器響應的 HTTP 狀態碼"status": 200,// `statusText` 來自服務器響應的 HTTP 狀態信息"statusText": "OK",// `headers` 是服務器響應頭// 所有的 header 名稱都是小寫,而且可以使用方括號語法訪問// 例如: `response.headers['content-type']`"headers": {},// `config` 是 `axios` 請求的配置信息"config": {},// `request` 是生成此響應的請求// 在node.js中它是最后一個ClientRequest實例 (in redirects),// 在瀏覽器中則是 XMLHttpRequest 實例"request": {}
}
下面是defaultResponseInterceptor
簡化代碼
type defaultResponseInterceptor = ({codeField,dataField,successCode,
}: {// 響應數據中,結果的字段名,用來判斷請求是否成功codeField: string;// 響應數據中,結果數據字段名,或者自定義解析dataField: ((response: any) => any) | string;// 響應數據中,自定義響應成功的codesuccessCode: ((code: any) => boolean) | number | string;
}) => {fulfilled;
};
執行defaultResponseInterceptor
返回一個對象,有 fulfilled 方法。
dataField
這個是用來指定返回 data 中實際需要的數據字段。
// raw ,body , data
config.responseReturn === "body";
在請求的 config 中,vben 自定義添加了responseReturn
字段,取值有三種,
- raw:返回原始數據
- body:返回 data 字段
- data:返回 data 字段中的數據
codeField
codeField 是指 上面 data 中的 code 字段。一般情況下,公司擁有自己一套請求成功還是失敗的一個判斷,基本都與業務相關聯。比如請求字段缺少,請求字段不合法等等,還有與業務相關的情況,比如訂單生成失敗的 code 等。
successCode
用來判斷響應的數據,是請求成功的,還是請求失敗。
在 vben 中的 config,一般使用的是responseReturn = "data"
。所以在頁面使用的時候,請求函數中最終得到的響應結果是 data 字段中的數據。
errorMessageResponseInterceptor
type errorMessageResponseInterceptor = (makeErrorMessage: (message: string, error: any) => void) => { rejected };
調用makeErrorMessage
方法,返回一個對象,對象中有 rejected 方法。
makeErrorMessage 方法中,處理了取消請求,請求超時,以及其他服務器返回的錯誤情況。如果對于請求成功,但是業務錯誤的情況,可以在請求成功響應攔截器中進行throw new Error(resp)
,然后通過makeErrorMessage
,自定義處理這中情況。
export const defaultResponseInterceptor = ({codeField = "code",dataField = "data",successCode = 0,
}: {/** 響應數據中代表訪問結果的字段名 */codeField: string;/** 響應數據中裝載實際數據的字段名,或者提供一個函數從響應數據中解析需要返回的數據 */dataField: ((response: any) => any) | string;/** 當codeField所指定的字段值與successCode相同時,代表接口訪問成功。如果提供一個函數,則返回true代表接口訪問成功 */successCode: ((code: any) => boolean) | number | string;
}): ResponseInterceptorConfig => {return {fulfilled: response => {const { config, data: responseData, status } = response;if (config.responseReturn === "raw") {return response;}if (status >= 200 && status < 400) {if (config.responseReturn === "body") {return responseData;} else if (isFunction(successCode) ? successCode(responseData[codeField]) : responseData[codeField] === successCode) {return isFunction(dataField) ? dataField(responseData) : responseData[dataField];}}throw Object.assign({}, response, { response });},};
};export const errorMessageResponseInterceptor = (makeErrorMessage?: MakeErrorMessageFn): ResponseInterceptorConfig => {return {rejected: (error: any) => {if (axios.isCancel(error)) {return Promise.reject(error);}const err: string = error?.toString?.() ?? "";let errMsg = "";if (err?.includes("Network Error")) {errMsg = $t("ui.fallback.http.networkError");} else if (error?.message?.includes?.("timeout")) {errMsg = $t("ui.fallback.http.requestTimeout");}if (errMsg) {makeErrorMessage?.(errMsg, error);return Promise.reject(error);}let errorMessage = "";const status = error?.response?.status;switch (status) {case 400: {errorMessage = $t("ui.fallback.http.badRequest");break;}case 401: {errorMessage = $t("ui.fallback.http.unauthorized");break;}case 403: {errorMessage = $t("ui.fallback.http.forbidden");break;}case 404: {errorMessage = $t("ui.fallback.http.notFound");break;}case 408: {errorMessage = $t("ui.fallback.http.requestTimeout");break;}default: {errorMessage = $t("ui.fallback.http.internalServerError");}}makeErrorMessage?.(errorMessage, error);return Promise.reject(error);},};
};