6 changed files with 251 additions and 73 deletions
@ -0,0 +1,48 @@ |
|||||
|
import { isCancel, AxiosRequestConfig, AxiosResponse, AxiosError } from "axios" |
||||
|
import { IPlugin } from "./base" |
||||
|
|
||||
|
declare module "axios" { |
||||
|
interface AxiosRequestConfig { |
||||
|
global?: boolean |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 取消重复请求
|
||||
|
export class RepeatPlugin implements IPlugin { |
||||
|
static name: string = "repeat" |
||||
|
name: string = "repeat" |
||||
|
pendingPool = new Map() |
||||
|
clearPendingPool(whiteList: string[] = []) { |
||||
|
if (!this.pendingPool.size) return |
||||
|
const pendingUrlList = Array.from(this.pendingPool.keys()).filter(url => !whiteList.includes(url)) |
||||
|
if (!pendingUrlList.length) return |
||||
|
|
||||
|
pendingUrlList.forEach(pendingUrl => { |
||||
|
if (!this.pendingPool.get(pendingUrl).global) { |
||||
|
this.pendingPool.get(pendingUrl).cancelFn(`${pendingUrl} 请求取消`) |
||||
|
this.pendingPool.delete(pendingUrl) |
||||
|
} |
||||
|
}) |
||||
|
return pendingUrlList |
||||
|
} |
||||
|
beforeRequestConfig({ config }: { config: AxiosRequestConfig }) { |
||||
|
const controller = new AbortController(); |
||||
|
config.signal = controller.signal |
||||
|
const cancelFn = (reason?: string) => controller.abort(reason) |
||||
|
this.pendingPool.has(config.url) |
||||
|
? cancelFn(`${config.url} 请求重复`) |
||||
|
: this.pendingPool.set(config.url, { cancelFn, global: config.global }) |
||||
|
} |
||||
|
beforeResponse({ response }: { response: AxiosResponse }) { |
||||
|
const { config } = response |
||||
|
this.pendingPool.delete(config.url) |
||||
|
} |
||||
|
beforeResponseError({ error }: { error: AxiosError }) { |
||||
|
const requestConfig = error.config! |
||||
|
if (!isCancel(error)) this.pendingPool.delete(requestConfig.url) |
||||
|
if (!error) return |
||||
|
if (isCancel(error)) { |
||||
|
throw new Error((requestConfig.signal as AbortSignal)?.reason || error.message || `请求'${requestConfig.url}' 被取消`) |
||||
|
} |
||||
|
} |
||||
|
} |
@ -1,62 +1,104 @@ |
|||||
import axios, { AxiosInstance, CreateAxiosDefaults } from "axios"; |
import axios, { AxiosInstance, CreateAxiosDefaults } from "axios"; |
||||
import { deepAssign } from "./utils"; |
import { deepAssign } from "./utils"; |
||||
|
|
||||
export const enum IHttpMethod { |
export interface IPlugin { |
||||
GET = "GET", |
name: string |
||||
POST = "POST", |
created?(...argus: any[]): void |
||||
DELETE = "DELETE", |
beforeCreate?(...argus: any[]): void |
||||
PUT = "PUT", |
beforeDestory?(...argus: any[]): void |
||||
|
beforeRequestConfig?(...argus: any[]): void |
||||
|
beforeRequestError?(...argus: any[]): void |
||||
|
beforeResponse?(...argus: any[]): void |
||||
|
beforeResponseError?(...argus: any[]): void |
||||
|
destory?(...argus: any[]): void |
||||
} |
} |
||||
|
|
||||
export enum IHttpContentType { |
export class Plugin implements IPlugin { |
||||
FORM = "application/x-www-form-urlencoded", |
name: string = "" |
||||
JSON = "application/json", |
created() { } |
||||
} |
beforeCreate() { } |
||||
|
beforeDestory() { } |
||||
class Plugin { |
beforeRequestConfig() { } |
||||
beforeInit() { } |
beforeRequestError() { } |
||||
|
beforeResponse() { } |
||||
|
beforeResponseError() { } |
||||
|
destory() { } |
||||
} |
} |
||||
|
|
||||
export abstract class PluginsManager { |
export abstract class PluginsManager { |
||||
static #plugins: Plugin[] = [] |
static plugins: IPlugin[] = [] |
||||
static use(plugin: Plugin) { |
static use(plugin: IPlugin) { |
||||
this.#plugins.push(plugin) |
if (Array.isArray(plugin)) { |
||||
} |
PluginsManager.plugins = this.plugins.concat(plugin) |
||||
|
} else PluginsManager.plugins.push(plugin) |
||||
|
} |
||||
|
|
||||
callPlugin(key: keyof Plugin, ...argus: any[]) { |
plugins: IPlugin[] = [] |
||||
PluginsManager.#plugins.forEach(p => p[key].apply(this, argus as [])) |
use(plugin: IPlugin) { |
||||
|
if (Array.isArray(plugin)) { |
||||
|
this.plugins = this.plugins.concat(plugin) |
||||
|
} else this.plugins.push(plugin) |
||||
|
} |
||||
|
callPluginByName<T>(name: string, key: keyof T, ...argus: any[]) { |
||||
|
const array = [...PluginsManager.plugins, ...this.plugins] |
||||
|
for (let i = 0; i < array.length; i++) { |
||||
|
let p = array[i] |
||||
|
if (!p.name) continue |
||||
|
if (name === p.name) { |
||||
|
const fn = (p as T)[key] |
||||
|
typeof fn === "function" && fn.apply(p, argus) |
||||
|
} |
||||
} |
} |
||||
|
} |
||||
|
callPlugin(key: keyof IPlugin, ...argus: any[]) { |
||||
|
const array = [...PluginsManager.plugins, ...this.plugins] |
||||
|
for (let i = 0; i < array.length; i++) { |
||||
|
let p = array[i] |
||||
|
const fn = p[key] |
||||
|
typeof fn === "function" && fn.apply(p, argus) |
||||
|
} |
||||
|
} |
||||
} |
} |
||||
|
|
||||
export abstract class httpBase extends PluginsManager { |
export abstract class httpBase extends PluginsManager { |
||||
static get method() { |
|
||||
return { |
|
||||
GET: "GET", |
|
||||
POST: "POST", |
|
||||
DELETE: "DELETE", |
|
||||
PUT: "PUT", |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
static get contentType() { |
instance: AxiosInstance | null = null |
||||
return { |
requestInterceptorId: null | number = null |
||||
FORM: "application/x-www-form-urlencoded", |
responseInterceptorId: null | number = null |
||||
JSON: "application/json", |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
initDefaultConfig: CreateAxiosDefaults = { |
|
||||
baseURL: process.env.VUE_APP_BASEURL, |
|
||||
timeout: 10000, |
|
||||
headers: { |
|
||||
'Content-Type': httpBase.contentType.FORM, |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
instance: AxiosInstance | null = null |
create<T>(config?: CreateAxiosDefaults<T>) { |
||||
|
this.instance = axios.create(deepAssign<CreateAxiosDefaults<T>>(axios.defaults, config ?? {})) |
||||
|
this.requestInterceptorId = this.instance.interceptors.request.use(config => { |
||||
|
const argu = { config } |
||||
|
this.callPlugin("beforeRequestConfig", argu) |
||||
|
return argu.config |
||||
|
}, error => { |
||||
|
const argu = { error } |
||||
|
this.callPlugin("beforeRequestError", argu) |
||||
|
return Promise.reject(error) |
||||
|
}) |
||||
|
this.responseInterceptorId = this.instance.interceptors.response.use(response => { |
||||
|
const argu = { response } |
||||
|
this.callPlugin("beforeResponse", argu) |
||||
|
return Promise.resolve(argu.response) |
||||
|
}, (error) => { |
||||
|
const argu = { error } |
||||
|
this.callPlugin("beforeResponseError", argu) |
||||
|
return Promise.reject(error) |
||||
|
}) |
||||
|
return this.instance |
||||
|
} |
||||
|
|
||||
create<T>(config?: CreateAxiosDefaults<T>) { |
destory() { |
||||
this.instance = axios.create(deepAssign<CreateAxiosDefaults<T>>(axios.defaults, this.initDefaultConfig, config ?? {})) |
// 清理一些事务
|
||||
return this.instance |
if (this.requestInterceptorId) { |
||||
|
this.instance?.interceptors.request.eject(this.requestInterceptorId) |
||||
|
} |
||||
|
if (this.responseInterceptorId) { |
||||
|
this.instance?.interceptors.response.eject(this.responseInterceptorId) |
||||
} |
} |
||||
|
httpBase.plugins = [] |
||||
|
this.plugins = [] |
||||
|
this.instance = null |
||||
|
} |
||||
} |
} |
||||
|
@ -1,37 +1,99 @@ |
|||||
import { httpBase, PluginsManager } from "./base" |
import { httpBase, IPlugin } from "./base" |
||||
|
import { CreateAxiosDefaults, AxiosRequestConfig, AxiosResponse } from "axios" |
||||
export const enum IHttpMethod { |
import { RepeatPlugin } from "./Repeat" |
||||
GET = "GET", |
|
||||
POST = "POST", |
|
||||
DELETE = "DELETE", |
|
||||
PUT = "PUT", |
|
||||
} |
|
||||
|
|
||||
export enum IHttpContentType { |
|
||||
FORM = "application/x-www-form-urlencoded", |
|
||||
JSON = "application/json", |
|
||||
} |
|
||||
|
|
||||
|
|
||||
const FlyPoll: Map<string, any> = new Map() |
const FlyPoll: Map<string, any> = new Map() |
||||
|
|
||||
class Fly extends httpBase { |
class Fly extends httpBase { |
||||
constructor() { |
static dispose(name: string) { |
||||
super() |
if (FlyPoll.has(name)) { |
||||
this.Init() |
const instance = FlyPoll.get(name) |
||||
|
instance.#destory() |
||||
|
FlyPoll.delete(name) |
||||
|
} |
||||
} |
} |
||||
static get(name?: string): Fly { |
static init(name: string, config: any, plugins: IPlugin[] = []) { |
||||
if (!name) name = "$defalt" |
|
||||
if (!FlyPoll.has(name)) { |
if (!FlyPoll.has(name)) { |
||||
FlyPoll.set(name, new Fly()) |
const instance = new Fly(name) |
||||
|
FlyPoll.set(name, instance) |
||||
|
instance.plugins = plugins |
||||
|
instance.#init(config || instance.default) |
||||
|
} |
||||
|
} |
||||
|
static invoke(name?: string): Fly { |
||||
|
if (!name) name = "$defalut" |
||||
|
const instance = FlyPoll.get(name) |
||||
|
if (!instance) throw new Error("未初始化此实例") |
||||
|
return instance |
||||
|
} |
||||
|
|
||||
|
static get method() { |
||||
|
return { |
||||
|
GET: "GET", |
||||
|
POST: "POST", |
||||
|
DELETE: "DELETE", |
||||
|
PUT: "PUT", |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
static get contentType() { |
||||
|
return { |
||||
|
FORM: "application/x-www-form-urlencoded", |
||||
|
JSON: "application/json", |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
default: CreateAxiosDefaults = { |
||||
|
// baseURL: process.env.VUE_APP_BASEURL,
|
||||
|
timeout: 10000, |
||||
|
headers: { |
||||
|
'Content-Type': Fly.contentType.FORM, |
||||
} |
} |
||||
return FlyPoll.get(name) |
|
||||
} |
} |
||||
Init() { |
|
||||
this.callPlugin("beforeInit") |
name: string |
||||
this.create() |
|
||||
|
constructor(name: string) { |
||||
|
super() |
||||
|
this.name = name |
||||
|
} |
||||
|
get(...argus: [url: string, config?: AxiosRequestConfig<unknown> | undefined]) { |
||||
|
return this.instance!.get.apply(this.instance, argus ?? []) as Promise<AxiosResponse> |
||||
|
} |
||||
|
request(...argus: [config: AxiosRequestConfig<unknown>]): Promise<AxiosResponse> { |
||||
|
return this.instance!.request.apply(this.instance, argus) as Promise<AxiosResponse> |
||||
|
} |
||||
|
#init(config: AxiosRequestConfig) { |
||||
|
this.callPlugin("beforeCreate") |
||||
|
this.create(config) |
||||
|
this.callPlugin("created") |
||||
|
} |
||||
|
#destory() { |
||||
|
this.callPlugin("beforeDestory") |
||||
|
this.destory() |
||||
|
this.callPlugin("destory") |
||||
} |
} |
||||
} |
} |
||||
|
|
||||
Fly.get() |
export { |
||||
|
Fly |
||||
|
} |
||||
|
|
||||
|
// https://api.52vmy.cn/
|
||||
|
// Fly.invoke().request({ method: "get", url: "http://localhost:5173/", global: false }).then((res) => console.log(res.data)).catch(console.log)
|
||||
|
// Fly.invoke().request({ method: "get", url: "http://localhost:5173/", global: true }).then((res) => console.log(res.data)).catch(console.log)
|
||||
|
|
||||
|
// Fly.invoke().request({ method: "get", url: "https://api.52vmy.cn/api/wl/word/bing/tu", global: true }).then((res) => console.log(res.data)).catch(console.log)
|
||||
|
|
||||
|
// Fly.invoke().request({ method: "get", url: "https://api.52vmy.cn/api/wl/word/bing/tu", global: true }).then((res) => console.log(res.data)).catch(console.log)
|
||||
|
|
||||
|
// Fly.invoke("empty").request({ method: "get", url: "https://api.52vmy.cn/api/wl/yan/bay" }).then((res) => console.log(res.data)).catch(console.log)
|
||||
|
|
||||
|
// Fly.invoke("empty").request({ method: "get", url: "https://api.52vmy.cn/api/img/tu/girl" }).then((res) => console.log(res.data)).catch(console.log)
|
||||
|
|
||||
|
|
||||
|
// setTimeout(()=>{
|
||||
|
// // Fly.invoke("empty").request({method: "get", url: "https://api.52vmy.cn/api/img/tu/man"}).then((res)=>console.log(res.data)).catch(console.log)
|
||||
|
|
||||
|
// Fly.invoke().callPluginByName<RepeatPlugin>(RepeatPlugin.name, "clearPendingPool")
|
||||
|
// }, 0)
|
||||
|
@ -0,0 +1,15 @@ |
|||||
|
import { Fly } from "./http" |
||||
|
import { RepeatPlugin } from "./http/Repeat" |
||||
|
|
||||
|
|
||||
|
Fly.init("$defalut", { |
||||
|
timeout: 10000, |
||||
|
}, [new RepeatPlugin()]) |
||||
|
|
||||
|
Fly.init("empty", { |
||||
|
timeout: 10000, |
||||
|
}) |
||||
|
|
||||
|
export { |
||||
|
Fly |
||||
|
} |
Loading…
Reference in new issue