import type { AxiosInstance, AxiosRequestConfig } from 'axios';
|
import axios from 'axios';
|
import { ElMessage } from 'element-plus';
|
import { Local, Session } from '/@/utils/storage';
|
import { AUTH_URL, MAIN_URL, SECONDARY_URL } from '/@/constants';
|
// import JSONbig from 'json-bigint';
|
|
//#region ====================== 后端 res.Code ======================
|
// // 摘要:
|
// // 成功
|
// Success = 0,
|
// //
|
// // 摘要:
|
// // 确认(权限验证使用)
|
// Confirm = -1,
|
// //
|
// // 摘要:
|
// // 提示(验证失败后使用)
|
// Prompt = -2,
|
// //
|
// // 摘要:
|
// // 警告(业务异常使用)
|
// Alert = -3,
|
// //
|
// // 摘要:
|
// // 错误(未捕获系统异常使用)
|
// Error = -4,
|
// //
|
// // 摘要:
|
// // 超时(暂不使用)
|
// TimeOut = -5
|
//#endregion
|
const initRequestInterceptor = (request: AxiosInstance) => {
|
// 添加请求拦截器
|
request.interceptors.request.use(
|
(config) => {
|
// // 在发送请求之前做些什么 token
|
// if (Local.get(accessTokenKey)) {
|
// (<any>config.headers).common['Authorization'] = `${Session.Local('token')}`;
|
// }
|
|
// 获取本地的 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);
|
const isExpired = new Date() >= exp;
|
// token 已经过期
|
if (isExpired) {
|
// 获取刷新 token
|
const refreshAccessToken = Local.get(refreshAccessTokenKey);
|
|
// 携带刷新 token
|
if (refreshAccessToken) {
|
config.headers!['X-Authorization'] = `Bearer ${refreshAccessToken}`;
|
}
|
}
|
// 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);
|
}
|
);
|
|
// 添加响应拦截器
|
request.interceptors.response.use(
|
(res) => {
|
// 获取状态码和返回数据
|
const status = res.status;
|
const serve = res.data;
|
// code 为 -1 就是权限验证失败
|
if (serve?.code === -1) {
|
clearAccessTokens();
|
window.location.reload();
|
}
|
// 处理 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 信息
|
|
// 只能叫 access-token
|
const accessToken = res.headers['access-token'];
|
// 只能叫 x-access-token'
|
const refreshAccessToken = res.headers['x-access-token'];
|
|
// 判断是否是无效 token
|
if (accessToken === 'invalid_token') {
|
// ElMessage.error('登录失效');
|
clearAccessTokens();
|
window.location.reload();
|
}
|
// 判断是否存在刷新 token,如果存在则存储在本地
|
else if (refreshAccessToken && accessToken && accessToken !== 'invalid_token') {
|
Local.set(accessTokenKey, accessToken);
|
Local.set(refreshAccessTokenKey, refreshAccessToken);
|
}
|
// 响应拦截及自定义处理
|
if (serve.data === 401) {
|
clearAccessTokens();
|
} else if (serve.code === undefined) {
|
return Promise.resolve(res.data);
|
// return res.data;
|
} else if (serve.code !== 200) {
|
const message = JSON.stringify(serve.message);
|
|
ElMessage.error(message);
|
throw new Error(message);
|
}
|
return res.data;
|
},
|
(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);
|
}
|
);
|
};
|
// 配置新建一个 axios 实例
|
const service = axios.create({
|
baseURL: MAIN_URL,
|
timeout: 50000,
|
headers: { 'Content-Type': 'application/json;charset=utf-8 ' },
|
// transformResponse: [
|
// function (data) {
|
// const JSONbigToString = JSONbig({ storeAsString: true });
|
// // 将Long类型数据转换为字符串
|
// return JSONbigToString.parse(data);
|
// },
|
// ],
|
});
|
|
initRequestInterceptor(service);
|
|
export function secondaryRequest(config: AxiosRequestConfig<any>) {
|
return service({
|
...config,
|
baseURL: SECONDARY_URL,
|
});
|
}
|
|
/**
|
* 用于访问登录接口
|
*/
|
export function authRequest(config: AxiosRequestConfig<any>) {
|
return service({
|
...config,
|
baseURL: AUTH_URL,
|
});
|
}
|
/**
|
* @description 域名前缀
|
* 防止类似于 http://sqi.beng35.com/airp 和 http://sqi.beng35.com/test 公用同一个 token 或 userInfo
|
*/
|
const subDomainName = window.location.pathname
|
.split('/')
|
.filter((item) => !!item)
|
.join('-');
|
const domainPrefix = subDomainName ? `${subDomainName}-` : '';
|
// token 键定义
|
export const accessTokenKey = domainPrefix + 'access-token';
|
export const refreshAccessTokenKey = `x-${accessTokenKey}`;
|
|
// userInfo键定义
|
export const userInfoKey = domainPrefix + 'userInfo';
|
|
// 获取 token
|
export const getToken = () => {
|
return Local.get(accessTokenKey);
|
};
|
|
// 清除 token
|
export const clearAccessTokens = async () => {
|
Local.remove(accessTokenKey);
|
Local.remove(refreshAccessTokenKey);
|
// 清除用户信息(每次刷新都需要利用用户信息去请求对应权限菜单)
|
Local.remove(userInfoKey);
|
// 清除其他
|
Session.clear();
|
};
|
|
// axios 默认实例
|
export const axiosInstance: AxiosInstance = axios;
|
|
/**
|
* 参数处理
|
* @param {*} params 参数
|
*/
|
export function tansParams(params: any) {
|
let result = '';
|
for (const propName of Object.keys(params)) {
|
const value = params[propName];
|
const 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') {
|
const params = propName + '[' + key + ']';
|
const 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, '+');
|
const 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);
|
}
|
|
// 导出 axios 实例
|
export default service;
|