import axios, { AxiosInstance, AxiosRequestConfig } from 'axios'; import { ElMessage } from 'element-plus'; import { Local, Session } from '/@/utils/storage'; // 定义请求中止控制器映射表 const abortControllerMap: Map<string, AbortController> = new Map(); // 配置新建一个 axios 实例 export const service = axios.create({ baseURL: window.__env__.VITE_API_URL as any, timeout: 50000, //headers: { 'Content-Type': 'application/json' }, 这个会导致生成代码的上传文件 file为空 }); // token 键定义 export const accessTokenKey = 'access-token'; export const refreshAccessTokenKey = `x-${accessTokenKey}`; // 获取 token export const getToken = () => { return Local.get(accessTokenKey); }; // 清除 token export const clearAccessTokens = () => { Local.remove(accessTokenKey); Local.remove(refreshAccessTokenKey); // 清除其他 Session.clear(); // 刷新浏览器 window.location.reload(); }; // axios 默认实例 export const axiosInstance: AxiosInstance = axios; // 添加请求拦截器 service.interceptors.request.use( (config) => { // // 在发送请求之前做些什么 token // if (Session.get('token')) { // (<any>config.headers).common['Authorization'] = `${Session.get('token')}`; // } // 记录中止控制信息 const controller = new AbortController(); config.signal = controller.signal; const url = config.url || ''; abortControllerMap.set(url, controller); // 获取本地的 token const accessToken = Local.get(accessTokenKey); if (accessToken) { // 将 token 添加到请求报文头中 config.headers!['Authorization'] = `Bearer ${accessToken}`; // 判断 accessToken 是否过期 const jwt: any = decryptJWT(accessToken); const exp = getJWTDate(jwt.exp as number); // token 已经过期 if (new Date() >= exp) { // 获取刷新 token const refreshAccessToken = Local.get(refreshAccessTokenKey); // 携带刷新 token if (refreshAccessToken) { config.headers!['X-Authorization'] = `Bearer ${refreshAccessToken}`; } } // debugger // get请求映射params参数 if (config.method?.toLowerCase() === 'get' && config.data) { let url = config.url + '?' + tansParams(config.data); url = url.slice(0, -1); config.data = {}; config.url = url; } } return config; }, (error) => { // 对请求错误做些什么 return Promise.reject(error); } ); // 添加响应拦截器 service.interceptors.response.use( (res) => { // 请求结束后清除中止控制项 const url = res.config.url || ''; abortControllerMap.delete(url); // 获取状态码和返回数据 var status = res.status; var serve = res.data; // 处理 401 if (status === 401) { clearAccessTokens(); } // 处理未进行规范化处理的 if (status >= 400) { throw new Error(res.statusText || 'Request Error.'); } // 处理规范化结果错误 if (serve && serve.hasOwnProperty('errors') && serve.errors) { throw new Error(JSON.stringify(serve.errors || 'Request Error.')); } // 读取响应报文头 token 信息 var accessToken = res.headers[accessTokenKey]; var refreshAccessToken = res.headers[refreshAccessTokenKey]; // 判断是否是无效 token if (accessToken === 'invalid_token') { clearAccessTokens(); } // 判断是否存在刷新 token,如果存在则存储在本地, 并重新加载页面 else if (refreshAccessToken && accessToken) { Local.set(accessTokenKey, accessToken); Local.set(refreshAccessTokenKey, refreshAccessToken); } // 响应拦截及自定义处理 if (serve.code === 401) { clearAccessTokens(); } else if (serve.code === undefined) { return Promise.resolve(res); } else if (serve.code !== 200) { var message; // 判断 serve.message 是否为对象 if (serve.message && typeof serve.message == 'object') { message = JSON.stringify(serve.message); } else { message = serve.message; } ElMessage({ dangerouslyUseHTMLString: true, message: message, type: 'error', }); throw new Error(message); } return res; }, (error) => { // 处理响应错误 if (error.response) { if (error.response.status === 401) { clearAccessTokens(); } } // 对响应错误做点什么 if (error.message.indexOf('timeout') != -1) { ElMessage.error('网络超时'); } else if (error.message == 'Network Error') { ElMessage.error('网络连接错误'); } else { if (error.response.data) ElMessage.error(error.response.statusText); else ElMessage.error('接口路径找不到'); } return Promise.reject(error); } ); // 取消指定请求 export const cancelRequest = (url: string | string[]) => { const urlList = Array.isArray(url) ? url : [url]; for (const _url of urlList) { abortControllerMap.get(_url)?.abort(); abortControllerMap.delete(_url); } } // 取消全部请求 export const cancelAllRequest = () => { for (const [_, controller] of abortControllerMap) { controller.abort(); } abortControllerMap.clear(); } /** * 参数处理 * @param {*} params 参数 */ export function tansParams(params: any) { let result = ''; for (const propName of Object.keys(params)) { const value = params[propName]; var part = encodeURIComponent(propName) + '='; if (value !== null && value !== '' && typeof value !== 'undefined') { if (typeof value === 'object') { for (const key of Object.keys(value)) { if (value[key] !== null && value[key] !== '' && typeof value[key] !== 'undefined') { let params = propName + '[' + key + ']'; var subPart = encodeURIComponent(params) + '='; result += subPart + encodeURIComponent(value[key]) + '&'; } } } else { result += part + encodeURIComponent(value) + '&'; } } } return result; } /** * 解密 JWT token 的信息 * @param token jwt token 字符串 * @returns <any>object */ export function decryptJWT(token: string): any { token = token.replace(/_/g, '/').replace(/-/g, '+'); var json = decodeURIComponent(escape(window.atob(token.split('.')[1]))); return JSON.parse(json); } /** * 将 JWT 时间戳转换成 Date * @description 主要针对 `exp`,`iat`,`nbf` * @param timestamp 时间戳 * @returns Date 对象 */ export function getJWTDate(timestamp: number): Date { return new Date(timestamp * 1000); } /** * Ajax请求,如果成功返回result字段,如果不成功提示错误信息 * @description Ajax请求 * @config AxiosRequestConfig 请求参数 * @returns 返回对象 */ export function request2(config: AxiosRequestConfig<any>): any { return new Promise((resolve, reject) => { service(config) .then((res) => { if (res.data.type == 'success') { resolve(res.data.result); } else { console.log('res', res); ElMessage.success(res.data.message); } }) .catch((res) => { console.log('res', res); ElMessage.error(res); reject(res); }); }); } // 导出 axios 实例 export default service;