2024-09-28 21:18:45 +08:00
|
|
|
|
import Axios, { type AxiosInstance, type AxiosRequestConfig } from 'axios';
|
2024-09-26 22:05:24 +08:00
|
|
|
|
import type { PureHttpError, PureHttpRequestConfig, PureHttpResponse, RequestMethods } from './types';
|
|
|
|
|
import NProgress from '@/utils/progress';
|
2024-09-28 21:18:45 +08:00
|
|
|
|
import { formatToken, getToken, removeToken } from '@/utils/auth';
|
2024-10-03 15:52:18 +08:00
|
|
|
|
import { useUserStoreHook } from '@/store/system/user';
|
2024-09-26 22:05:24 +08:00
|
|
|
|
import { message } from '@/utils/message';
|
2024-09-28 21:18:45 +08:00
|
|
|
|
import { router } from '@/store/utils';
|
|
|
|
|
import { defaultConfig, whiteList } from '@/api/service/config';
|
2024-09-26 09:38:02 +08:00
|
|
|
|
|
|
|
|
|
class PureHttp {
|
2024-09-26 22:05:24 +08:00
|
|
|
|
/** `token`过期后,暂存待执行的请求 */
|
|
|
|
|
private static requests = [];
|
|
|
|
|
/** 防止重复刷新`token` */
|
|
|
|
|
private static isRefreshing = false;
|
|
|
|
|
/** 初始化配置对象 */
|
|
|
|
|
private static initConfig: PureHttpRequestConfig = {};
|
|
|
|
|
/** 保存当前`Axios`实例对象 */
|
|
|
|
|
private static axiosInstance: AxiosInstance = Axios.create(defaultConfig);
|
2024-09-26 09:38:02 +08:00
|
|
|
|
|
2024-09-26 22:05:24 +08:00
|
|
|
|
constructor() {
|
|
|
|
|
this.httpInterceptorsRequest();
|
|
|
|
|
this.httpInterceptorsResponse();
|
|
|
|
|
}
|
2024-09-26 09:38:02 +08:00
|
|
|
|
|
2024-09-26 22:05:24 +08:00
|
|
|
|
/** 重连原始请求 */
|
|
|
|
|
private static retryOriginalRequest(config: PureHttpRequestConfig) {
|
|
|
|
|
return new Promise(resolve => {
|
|
|
|
|
PureHttp.requests.push((token: string) => {
|
|
|
|
|
config.headers['token'] = formatToken(token);
|
|
|
|
|
resolve(config);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}
|
2024-09-26 09:38:02 +08:00
|
|
|
|
|
2024-09-26 22:05:24 +08:00
|
|
|
|
/** 通用请求工具函数 */
|
|
|
|
|
public request<T>(method: RequestMethods, url: string, param?: AxiosRequestConfig, axiosConfig?: PureHttpRequestConfig): Promise<T> {
|
2024-09-27 09:33:50 +08:00
|
|
|
|
const config = { method, url, ...param, ...axiosConfig } as PureHttpRequestConfig;
|
2024-09-26 09:38:02 +08:00
|
|
|
|
|
2024-09-26 22:05:24 +08:00
|
|
|
|
// 单独处理自定义请求/响应回调
|
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
|
PureHttp.axiosInstance
|
|
|
|
|
.request(config)
|
|
|
|
|
.then((response: undefined) => {
|
|
|
|
|
resolve(response);
|
|
|
|
|
})
|
|
|
|
|
.catch(error => {
|
|
|
|
|
reject(error);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}
|
2024-09-26 09:38:02 +08:00
|
|
|
|
|
2024-09-26 22:05:24 +08:00
|
|
|
|
/** 单独抽离的`post`工具函数 */
|
|
|
|
|
public post<T, P>(url: string, params?: AxiosRequestConfig<P>, config?: PureHttpRequestConfig): Promise<T> {
|
|
|
|
|
return this.request<T>('post', url, params, config);
|
|
|
|
|
}
|
2024-09-26 09:38:02 +08:00
|
|
|
|
|
2024-09-26 22:05:24 +08:00
|
|
|
|
/** 单独抽离的`get`工具函数 */
|
|
|
|
|
public get<T, P>(url: string, params?: AxiosRequestConfig<P>, config?: PureHttpRequestConfig): Promise<T> {
|
|
|
|
|
return this.request<T>('get', url, params, config);
|
|
|
|
|
}
|
2024-09-26 09:38:02 +08:00
|
|
|
|
|
2024-09-26 22:05:24 +08:00
|
|
|
|
/** 请求拦截 */
|
|
|
|
|
private httpInterceptorsRequest(): void {
|
|
|
|
|
PureHttp.axiosInstance.interceptors.request.use(
|
|
|
|
|
async (config: PureHttpRequestConfig): Promise<any> => {
|
|
|
|
|
// 开启进度条动画
|
|
|
|
|
NProgress.start();
|
|
|
|
|
// 优先判断post/get等方法是否传入回调,否则执行初始化设置等回调
|
|
|
|
|
if (typeof config.beforeRequestCallback === 'function') {
|
|
|
|
|
config.beforeRequestCallback(config);
|
|
|
|
|
return config;
|
|
|
|
|
}
|
|
|
|
|
if (PureHttp.initConfig.beforeRequestCallback) {
|
|
|
|
|
PureHttp.initConfig.beforeRequestCallback(config);
|
|
|
|
|
return config;
|
|
|
|
|
}
|
|
|
|
|
/** 请求白名单,放置一些不需要`token`的接口(通过设置请求白名单,防止`token`过期后再请求造成的死循环问题) */
|
|
|
|
|
return whiteList.some(url => config.url.endsWith(url))
|
|
|
|
|
? config
|
|
|
|
|
: new Promise(resolve => {
|
|
|
|
|
const data = getToken();
|
2024-09-27 09:33:50 +08:00
|
|
|
|
// 存在token
|
2024-09-26 22:05:24 +08:00
|
|
|
|
if (data) {
|
|
|
|
|
const now = new Date().getTime();
|
|
|
|
|
const expired = parseInt(data.expires) - now <= 0;
|
|
|
|
|
if (expired) {
|
|
|
|
|
if (!PureHttp.isRefreshing) {
|
|
|
|
|
PureHttp.isRefreshing = true;
|
|
|
|
|
// token过期刷新
|
|
|
|
|
useUserStoreHook()
|
|
|
|
|
.handRefreshToken({ refreshToken: data.refreshToken })
|
|
|
|
|
.then((res: any) => {
|
2024-09-27 09:33:50 +08:00
|
|
|
|
// 从结果中获取token
|
|
|
|
|
const token = res.data.token;
|
2024-09-26 22:05:24 +08:00
|
|
|
|
config.headers['token'] = formatToken(token);
|
|
|
|
|
PureHttp.requests.forEach(cb => cb(token));
|
|
|
|
|
PureHttp.requests = [];
|
|
|
|
|
})
|
|
|
|
|
.finally(() => {
|
|
|
|
|
PureHttp.isRefreshing = false;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
resolve(PureHttp.retryOriginalRequest(config));
|
|
|
|
|
} else {
|
2024-09-27 09:33:50 +08:00
|
|
|
|
config.headers['token'] = formatToken(data.token);
|
2024-09-26 22:05:24 +08:00
|
|
|
|
resolve(config);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
resolve(config);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
error => error,
|
|
|
|
|
);
|
|
|
|
|
}
|
2024-09-26 09:38:02 +08:00
|
|
|
|
|
2024-09-26 22:05:24 +08:00
|
|
|
|
/** 响应拦截 */
|
|
|
|
|
private httpInterceptorsResponse(): void {
|
|
|
|
|
const instance = PureHttp.axiosInstance;
|
|
|
|
|
instance.interceptors.response.use(
|
2024-10-15 15:24:52 +08:00
|
|
|
|
async (response: PureHttpResponse) => {
|
2024-09-26 22:05:24 +08:00
|
|
|
|
const $config = response.config;
|
2024-09-28 21:18:45 +08:00
|
|
|
|
const data = response.data;
|
|
|
|
|
|
2024-09-26 22:05:24 +08:00
|
|
|
|
// 关闭进度条动画
|
|
|
|
|
NProgress.done();
|
2024-09-28 21:18:45 +08:00
|
|
|
|
|
|
|
|
|
// 登录过期,和异常处理
|
|
|
|
|
if (data.code === 208) {
|
|
|
|
|
message(data.message, { type: 'warning' });
|
|
|
|
|
removeToken();
|
2024-10-15 15:24:52 +08:00
|
|
|
|
await router.push('/login');
|
2024-09-30 16:21:41 +08:00
|
|
|
|
} else if (data.code >= 201 && data.code < 300) {
|
2024-09-28 21:18:45 +08:00
|
|
|
|
message(data.message, { type: 'warning' });
|
|
|
|
|
} else if (data.code > 300) {
|
|
|
|
|
message(data.message, { type: 'error' });
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-26 22:05:24 +08:00
|
|
|
|
// 优先判断post/get等方法是否传入回调,否则执行初始化设置等回调
|
|
|
|
|
if (typeof $config.beforeResponseCallback === 'function') {
|
|
|
|
|
$config.beforeResponseCallback(response);
|
2024-09-28 21:18:45 +08:00
|
|
|
|
return data;
|
2024-09-26 22:05:24 +08:00
|
|
|
|
}
|
2024-09-28 21:18:45 +08:00
|
|
|
|
|
2024-09-26 22:05:24 +08:00
|
|
|
|
if (PureHttp.initConfig.beforeResponseCallback) {
|
|
|
|
|
PureHttp.initConfig.beforeResponseCallback(response);
|
2024-09-28 21:18:45 +08:00
|
|
|
|
return data;
|
2024-09-26 22:05:24 +08:00
|
|
|
|
}
|
2024-09-28 21:18:45 +08:00
|
|
|
|
|
|
|
|
|
return data;
|
2024-09-26 22:05:24 +08:00
|
|
|
|
},
|
|
|
|
|
(error: PureHttpError) => {
|
2024-09-28 21:18:45 +08:00
|
|
|
|
error.isCancelRequest = Axios.isCancel(error);
|
|
|
|
|
|
2024-09-26 22:05:24 +08:00
|
|
|
|
// 关闭进度条动画
|
|
|
|
|
NProgress.done();
|
2024-10-08 10:44:31 +08:00
|
|
|
|
message(error.message, { type: 'error' });
|
2024-09-28 21:18:45 +08:00
|
|
|
|
return error;
|
2024-09-26 22:05:24 +08:00
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
}
|
2024-09-26 09:38:02 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export const http = new PureHttp();
|