/** * 当前版本:v1.4.0 * 使用描述:https://editor.swagger.io 代码生成 typescript-axios 辅组工具库 * 依赖说明:适配 axios 版本:v0.21.4 * 视频教程:https://www.bilibili.com/video/BV1EW4y1C71D */ import globalAxios, { AxiosInstance } from 'axios'; import { Configuration } from '../api-services'; import { BaseAPI, BASE_PATH } from '../api-services/base'; import { ElMessage } from 'element-plus'; import { Local, Session } from '../utils/storage'; // 接口服务器配置 export const serveConfig = new Configuration({ basePath: window.__env__.VITE_API_URL, }); // token 键定义 export const accessTokenKey = 'access-token'; export const refreshAccessTokenKey = `x-${accessTokenKey}`; // 获取 token export const getToken = () => { return Local.get(accessTokenKey); }; // 获取请求头 token export const getHeader = () => { return { authorization: 'Bearer ' + getToken() }; }; // 清除 token export const clearAccessTokens = () => { clearTokens(); // 刷新浏览器 window.location.reload(); }; // 清除 token export const clearTokens = () => { Local.remove(accessTokenKey); Local.remove(refreshAccessTokenKey); Session.clear(); }; // axios 默认实例 export const axiosInstance: AxiosInstance = globalAxios; // 这里可以配置 axios 更多选项 ========================================= axiosInstance.defaults.timeout = 1000 * 60 * 10; // 设置超时,默认 10 分钟 // axios 请求拦截 axiosInstance.interceptors.request.use( (conf) => { // 获取本地的 token或session中的token const accessToken = Local.get(accessTokenKey) ? Local.get(accessTokenKey) : Session.get('token'); if (accessToken) { // 将 token 添加到请求报文头中 conf.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) { conf.headers!['X-Authorization'] = `Bearer ${refreshAccessToken}`; } } } // 这里编写请求拦截代码 ========================================= // 获取前端设置的语言 const globalI18n = Local.get('themeConfig')?.globalI18n; if (globalI18n) { // 添加到请求报文头中 conf.headers!['Accept-Language'] = globalI18n; } return conf; }, (error) => { // 处理请求错误 if (error.request) { ElMessage.error(error); } // 请求错误代码及自定义处理 ElMessage.error(error); return Promise.reject(error); } ); // axios 响应拦截 axiosInstance.interceptors.response.use( (res) => { // 获取状态码和返回数据 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 && accessToken !== 'invalid_token') { 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; } // 用户自定义处理异常 if (!res.config?.customCatch) { ElMessage.error(message); } throw new Error(message); } return res; }, (error) => { // 处理响应错误 if (error.response) { if (error.response.status === 401) { clearAccessTokens(); } } // 用户自定义处理异常 if (!error.config?.customCatch) { // 响应错误代码及自定义处理 ElMessage.error(error); } return Promise.reject(error); } ); /** * 包装 Promise 并返回 [Error, any] * @param promise Promise 方法 * @param errorExt 自定义错误信息(拓展) * @returns [Error, any] */ export function feature(promise: Promise, errorExt?: object): Promise<[U, undefined] | [null, T]> { return promise .then<[null, T]>((data: T) => [null, data]) .catch<[U, undefined]>((err: U) => { if (errorExt) { const parsedError = Object.assign({}, err, errorExt); return [parsedError, undefined]; } return [err, undefined]; }); } /** * 获取/创建服务 API 实例 * @param apiType BaseAPI 派生类型 * @param configuration 服务器配置对象 * @param basePath 服务器地址 * @param axiosObject axios 实例 * @returns 服务API 实例 */ export function getAPI( // eslint-disable-next-line no-unused-vars apiType: new (configuration?: Configuration, basePath?: string, axiosInstance?: AxiosInstance) => T, configuration: Configuration = serveConfig, basePath: string = BASE_PATH, axiosObject: AxiosInstance = axiosInstance ) { return new apiType(configuration, basePath, axiosObject); } /** * 解密 JWT token 的信息 * @param token jwt token 字符串 * @returns 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); } /** * 实现异步延迟 * @param delay 延迟时间(毫秒) * @returns */ export function sleep(delay: number) { return new Promise((resolve) => setTimeout(resolve, delay)); }