vue3+ts 简单封装axios:实现错误重试、重复请求取消、手动取消
注意:需要安装crypto-js库
-
在
utils\request.ts中:import axios from "axios"; import type { InternalAxiosRequestConfig, AxiosResponse, AxiosInstance } from "axios"; import { useRequeryStore } from "@/store/request"; const useRequeryStoreInfo = useRequeryStore(); let isLogoutShowing = false; // 登录过期弹窗锁 // 创建Axios实例 const axiosInstance: AxiosInstance = axios.create({ baseURL: import.meta.env.VITE_BASE_URL, timeout: 60000 // 延时时间:60s }); // 请求拦截器 axiosInstance.interceptors.request.use( (config: InternalAxiosRequestConfig) => { // 添加token let token = useRequeryStoreInfo.getToken; if (token) config.headers["Authorization"] = "Bearer " + token; const key = useRequeryStoreInfo.getRequestKey(config); // 移除旧的请求 useRequeryStoreInfo.removeOldRequest(key); // 开始新的请求 const controller = new AbortController(); config.signal = controller.signal; useRequeryStoreInfo.setRequest(key, controller); return config; }, (error) => { return Promise.reject(error); } ); // 响应拦截器 axiosInstance.interceptors.response.use( (response: AxiosResponse) => { // 移除完成的请求 useRequeryStoreInfo.removeNewRequest(response.config); // 错误处理 if (response.data.code != 200) { responseError(response.data.code, response.data.message); return Promise.reject(response.data); } let config = response.config; console.log(`%c${config.baseURL}${config.url} %c${config.method?.toUpperCase()}`, "color:#409EFF; font-weight:bold;", "color:#67C23A; font-weight:bold;"); console.log("- 请求参数", config.data, config.params); console.log("- 响应结果", response.data); return Promise.resolve(response.data); }, async (error) => { let config = error.config; // 移除完成的请求、不包括主动取消 if (error.code !== "ERR_CANCELED" && config) useRequeryStoreInfo.removeNewRequest(error.config); // 错误重试 if (config.errorRetry) { const retryTotal: number = config.retryTotal ?? 3; const retryCount: number = config.retryCount ?? 0; if (retryCount < retryTotal) { ElMessage.warning(`请求失败,正在进行第${retryCount + 1}次尝试重新请求...`); config.retryCount = retryCount + 1; await new Promise((resolve) => setTimeout(resolve, 1000)); const newController = new AbortController(); config.signal = newController.signal; return axiosInstance(config); } } // 错误提示处理 if (error.code !== "ERR_CANCELED") { let { message } = error; if (message == "Network Error") message = "连接异常,请稍后重试..."; else if (message.includes("timeout")) message = "请求超时,请稍后重试..."; ElMessage.error(message || "未知错误,请联系管理员!"); } return Promise.reject(error); } ); // 响应错误处理 const responseError = (code: number, message: string) => { switch (code) { case 401: if (isLogoutShowing) return; isLogoutShowing = true; ElMessageBox.confirm("登录过期,是否重新登录?", "Warning", { confirmButtonText: "重新登录", cancelButtonText: "取消", type: "warning" }) .then(() => { useRequeryStoreInfo.logout(); }) .finally(() => { isLogoutShowing = false; }); break; default: ElMessage.error(message || "未知错误,请联系管理员!"); break; } }; export default axiosInstance; -
在
store\request.ts中:import { defineStore } from "pinia"; import { MD5 } from "crypto-js"; import router from "@/router"; /** * 网络请求 * requestList:网络请求 */ interface Config { method?: string; url?: string; data?: any; params?: any; } interface State { token: string; requestList: Map<string, AbortController>; } export const useRequeryStore = defineStore("requery", { persist: { pick: ["token"] }, state: (): State => ({ token: "", requestList: new Map() }), getters: { // 获取requestList getRequestList: (state) => state.requestList, // 获取Token getToken: (state) => state.token }, actions: { // 重新登录 logout() { router.push({ name: "login" }); localStorage.clear(); sessionStorage.clear(); }, // 设置Token setToken(token: string) { this.token = token; }, /** * 获取请求key * @param {Config} config 需要method, data, url, params数据 * @returns {string} key */ getRequestKey(config: Config): string { const { method, url, data, params } = config; let newData = typeof data === "object" ? JSON.stringify(data) : data; let newParams = typeof params === "object" ? JSON.stringify(params) : params; return MD5([method, url, newParams, newData].join("&")).toString(); }, /** * 取消请求 * @param {string} key 通过getRequestKey生成的key * @param {boolean} isMessage 是否需要提示 * @param {boolean} message 提示内容 */ cancelRequest(key: string, isMessage: boolean = false, message: string = "请勿重复操作!") { if (this.getRequestList.has(key)) { const controller = this.requestList.get(key); if (controller) { controller.abort(); this.requestList.delete(key); } if (isMessage) ElMessage.warning(message); } }, // 存储请求数据 setRequest(key: string, controller: AbortController) { this.requestList.set(key, controller); }, // 移除旧的请求 removeOldRequest(key: string) { this.cancelRequest(key, true); }, // 移除完成的请求 removeNewRequest(config: Config) { const key = this.getRequestKey(config); this.getRequestList.delete(key); }, /** * 手动取消请求 * @param {Config} config 需要method, data, url, params数据,请和请求的时候一模一样 */ manualCancelRequest(config: Config) { const key = this.getRequestKey(config); this.cancelRequest(key, true, "成功取消请求!"); } } }); -
封装请求接口:在
api\login.ts中import request from "@/utils/request"; // 登录 export const getLogin = (data?: any) => { return request({ url: "/user/login", method: "post", data }); }; -
错误重复请求:在
api\login.ts中import request from "@/utils/request"; // 登录 export const getLogin = (data?: any) => { return request({ url: "/user/login", method: "post", data, errorRetry:true, retryTotal:3, }); };
本文来自博客园,作者:小周同学~,转载请注明原文链接:https://www.cnblogs.com/xiaozhou-wuyu/p/19199892

浙公网安备 33010602011771号