2023-07-12 16:08:00 +08:00

221 lines
6.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import Axios, {
AxiosInstance,
AxiosRequestConfig,
CustomParamsSerializer
} from "axios";
import {
PureHttpError,
RequestMethods,
PureHttpResponse,
PureHttpRequestConfig
} from "./types.d";
import { stringify } from "qs";
import NProgress from "../progress";
import { getToken, formatToken } from "@/utils/auth";
import { message } from "../message";
import { ElMessageBox } from "element-plus";
import { router } from "@/router";
import { removeToken } from "@/utils/auth";
// console.log("Utils:" + router);
const { VITE_APP_BASE_API } = import.meta.env;
// 相关配置请参考www.axios-js.com/zh-cn/docs/#axios-request-config-1
const defaultConfig: AxiosRequestConfig = {
// 请求超时时间
timeout: 10000,
// 后端请求地址
baseURL: VITE_APP_BASE_API,
headers: {
Accept: "application/json, text/plain, */*",
"Content-Type": "application/json",
"X-Requested-With": "XMLHttpRequest"
},
// 数组格式参数序列化https://github.com/axios/axios/issues/5142
paramsSerializer: {
serialize: stringify as unknown as CustomParamsSerializer
}
};
class PureHttp {
constructor() {
this.httpInterceptorsRequest();
this.httpInterceptorsResponse();
}
/** token过期后暂存待执行的请求 */
private static requests = [];
/** 防止重复刷新token */
private static isRefreshing = false;
/** 初始化配置对象 */
private static initConfig: PureHttpRequestConfig = {};
/** 保存当前Axios实例对象 */
private static axiosInstance: AxiosInstance = Axios.create(defaultConfig);
/** 重连原始请求 */
private static retryOriginalRequest(config: PureHttpRequestConfig) {
return new Promise(resolve => {
PureHttp.requests.push((token: string) => {
config.headers["Authorization"] = formatToken(token);
resolve(config);
});
});
}
/** 请求拦截 */
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过期后再请求造成的死循环问题 */
const whiteList = [
"/refreshToken",
"/login",
"/captchaImage",
"/getConfig"
];
return whiteList.some(v => config.url.indexOf(v) > -1)
? config
: new Promise(resolve => {
const data = getToken();
config.headers["Authorization"] = formatToken(data.token);
resolve(config);
});
},
error => {
return Promise.reject(error);
}
);
}
/** 响应拦截 */
private httpInterceptorsResponse(): void {
const instance = PureHttp.axiosInstance;
instance.interceptors.response.use(
(response: PureHttpResponse) => {
// 请求返回失败时,有业务错误时,弹出错误提示
if (response.data.code !== 0) {
// token失效时弹出过期提示
if (response.data.code === 20101) {
ElMessageBox.confirm(
"登录状态已过期,您可以继续留在该页面,或者重新登录",
"系统提示",
{
confirmButtonText: "重新登录",
cancelButtonText: "取消",
type: "warning"
}
)
.then(() => {
removeToken();
router.push("/login");
})
.catch(() => {
message("取消重新登录", { type: "info" });
});
} else {
// 其余情况弹出错误提示框
message(response.data.msg, { type: "error" });
NProgress.done();
return Promise.reject(response.data.msg);
}
}
const $config = response.config;
// 关闭进度条动画
NProgress.done();
// 优先判断post/get等方法是否传入回调否则执行初始化设置等回调
if (typeof $config.beforeResponseCallback === "function") {
$config.beforeResponseCallback(response);
return response.data;
}
if (PureHttp.initConfig.beforeResponseCallback) {
PureHttp.initConfig.beforeResponseCallback(response);
return response.data;
}
return response.data;
},
(error: PureHttpError) => {
const $error = error;
$error.isCancelRequest = Axios.isCancel($error);
// 关闭进度条动画
NProgress.done();
// 所有的响应异常 区分来源为取消请求/非取消请求
return Promise.reject($error);
}
);
}
/** 通用请求工具函数 */
public request<T>(
method: RequestMethods,
url: string,
param?: AxiosRequestConfig,
axiosConfig?: PureHttpRequestConfig
): Promise<T> {
const config = {
method,
url,
...param,
...axiosConfig
} as PureHttpRequestConfig;
// 单独处理自定义请求/响应回调
return new Promise((resolve, reject) => {
PureHttp.axiosInstance
.request(config)
.then((response: undefined) => {
resolve(response);
})
.catch(error => {
// 某些情况网络失效此时直接进入error流程所以在这边也进行拦截
if (error.response && error.response.status >= 500) {
message("网络异常", { type: "error" });
}
if (
error.response &&
error.response.status >= 400 &&
error.response.status < 500
) {
message("请求接口不存在", { type: "error" });
}
reject(error);
});
});
}
/** 单独抽离的post工具函数 */
public post<T, P>(
url: string,
params?: AxiosRequestConfig<T>,
config?: PureHttpRequestConfig
): Promise<P> {
return this.request<P>("post", url, params, config);
}
/** 单独抽离的get工具函数 */
public get<T, P>(
url: string,
params?: AxiosRequestConfig<T>,
config?: PureHttpRequestConfig
): Promise<P> {
return this.request<P>("get", url, params, config);
}
}
export const http = new PureHttp();