import Axios, { AxiosRequestConfig, CancelTokenStatic, AxiosInstance, Canceler } from "axios" import NProgress from "../progress" import { genConfig } from "./config" import { transformConfigByMethod } from "./utils" import { cancelTokenType, RequestMethods, EnclosureHttpRequestConfig, EnclosureHttpResoponse, EnclosureHttpError } from "./types.d" class EnclosureHttp { constructor() { this.httpInterceptorsRequest() this.httpInterceptorsResponse() } // 初始化配置对象 private static initConfig: EnclosureHttpRequestConfig = {}; // 保存当前Axios实例对象 private static axiosInstance: AxiosInstance = Axios.create(genConfig()); // 保存 EnclosureHttp实例 private static EnclosureHttpInstance: EnclosureHttp // axios取消对象 private CancelToken: CancelTokenStatic = Axios.CancelToken; // 取消的凭证数组 private sourceTokenList: Array = []; // 记录当前这一次cancelToken的key private currentCancelTokenKey = ""; private beforeRequestCallback: EnclosureHttpRequestConfig["beforeRequestCallback"] = undefined; private beforeResponseCallback: EnclosureHttpRequestConfig["beforeResponseCallback"] = undefined; public get cancelTokenList(): Array { return this.sourceTokenList } // eslint-disable-next-line class-methods-use-this public set cancelTokenList(value) { throw new Error("cancelTokenList不允许赋值") } /** * @description 私有构造不允许实例化 * @returns void 0 */ // constructor() {} /** * @description 生成唯一取消key * @param config axios配置 * @returns string */ // eslint-disable-next-line class-methods-use-this private genUniqueKey(config: EnclosureHttpRequestConfig): string { return `${config.url}--${JSON.stringify(config.data)}` } /** * @description 取消重复请求 * @returns void 0 */ private cancelRepeatRequest(): void { const temp: { [key: string]: boolean } = {} this.sourceTokenList = this.sourceTokenList.reduce>( (res: Array, cancelToken: cancelTokenType) => { const { cancelKey, cancelExecutor } = cancelToken if (!temp[cancelKey]) { temp[cancelKey] = true res.push(cancelToken) } else { cancelExecutor() } return res }, [] ) } /** * @description 删除指定的CancelToken * @returns void 0 */ private deleteCancelTokenByCancelKey(cancelKey: string): void { this.sourceTokenList = this.sourceTokenList.length < 1 ? this.sourceTokenList.filter( cancelToken => cancelToken.cancelKey !== cancelKey ) : [] } /** * @description 拦截请求 * @returns void 0 */ private httpInterceptorsRequest(): void { EnclosureHttp.axiosInstance.interceptors.request.use( (config: EnclosureHttpRequestConfig) => { const $config = config NProgress.start() // 每次切换页面时,调用进度条 const cancelKey = this.genUniqueKey($config) $config.cancelToken = new this.CancelToken((cancelExecutor: (cancel: any) => void) => { this.sourceTokenList.push({ cancelKey, cancelExecutor }) }) this.cancelRepeatRequest() this.currentCancelTokenKey = cancelKey // 优先判断post/get等方法是否传入回掉,否则执行初始化设置等回掉 if (typeof this.beforeRequestCallback === "function") { this.beforeRequestCallback($config) this.beforeRequestCallback = undefined return $config } if (EnclosureHttp.initConfig.beforeRequestCallback) { EnclosureHttp.initConfig.beforeRequestCallback($config) return $config } return $config }, error => { return Promise.reject(error) } ) } /** * @description 清空当前cancelTokenList * @returns void 0 */ public clearCancelTokenList(): void { this.sourceTokenList.length = 0 } /** * @description 拦截相应 * @returns void 0 */ private httpInterceptorsResponse(): void { const instance = EnclosureHttp.axiosInstance instance.interceptors.response.use( (response: EnclosureHttpResoponse) => { // 请求每次成功一次就删除当前canceltoken标记 const cancelKey = this.genUniqueKey(response.config) this.deleteCancelTokenByCancelKey(cancelKey) // 优先判断post/get等方法是否传入回掉,否则执行初始化设置等回掉 if (typeof this.beforeResponseCallback === "function") { this.beforeResponseCallback(response) this.beforeResponseCallback = undefined return response.data } if (EnclosureHttp.initConfig.beforeResponseCallback) { EnclosureHttp.initConfig.beforeResponseCallback(response) return response.data } NProgress.done() return response.data }, (error: EnclosureHttpError) => { const $error = error // 判断当前的请求中是否在 取消token数组理存在,如果存在则移除(单次请求流程) if (this.currentCancelTokenKey) { const haskey = this.sourceTokenList.filter( cancelToken => cancelToken.cancelKey === this.currentCancelTokenKey ).length if (haskey) { this.sourceTokenList = this.sourceTokenList.filter( cancelToken => cancelToken.cancelKey !== this.currentCancelTokenKey ) this.currentCancelTokenKey = "" } } $error.isCancelRequest = Axios.isCancel($error) // 所有的响应异常 区分来源为取消请求/非取消请求 return Promise.reject($error) } ) } public request( method: RequestMethods, url: string, param?: AxiosRequestConfig, axiosConfig?: EnclosureHttpRequestConfig, ): Promise { const config = transformConfigByMethod(param, { method, url, ...axiosConfig } as EnclosureHttpRequestConfig) // 单独处理自定义请求/响应回掉 if (axiosConfig?.beforeRequestCallback) { this.beforeRequestCallback = axiosConfig.beforeRequestCallback } if (axiosConfig?.beforeResponseCallback) { this.beforeResponseCallback = axiosConfig.beforeResponseCallback } return new Promise((resolve, reject) => { EnclosureHttp.axiosInstance.request(config) .then((response: EnclosureHttpResoponse) => { resolve(response) }) .catch((error: any) => { reject(error) }) }) } public post( url: string, params?: T, config?: EnclosureHttpRequestConfig ): Promise { return this.request("post", url, params, config) } public get( url: string, params?: T, config?: EnclosureHttpRequestConfig ): Promise { return this.request("get", url, params, config) } } export default EnclosureHttp