feat: 支持清空拦截器
parent
09ac2a94e2
commit
cbcc43ad77
|
@ -9,6 +9,7 @@
|
||||||
"root": true,
|
"root": true,
|
||||||
"rules": {
|
"rules": {
|
||||||
"@typescript-eslint/no-explicit-any": 0,
|
"@typescript-eslint/no-explicit-any": 0,
|
||||||
"@typescript-eslint/no-non-null-assertion": 0
|
"@typescript-eslint/no-non-null-assertion": 0,
|
||||||
|
"@typescript-eslint/ban-ts-comment": 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -259,7 +259,7 @@ axios.defaults.adapter = axios.createAdapter({
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
可以使用 `createAdapter` 彻底抹平存在差异的部分,实现全平台完美适配。
|
可以进一步抹平存在差异的部分,实现完美适配全平台。
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
import axios from 'axios-miniprogram';
|
import axios from 'axios-miniprogram';
|
||||||
|
|
|
@ -337,7 +337,7 @@ export function createAdapter(platform: AxiosPlatform) {
|
||||||
return adapter;
|
return adapter;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isPlatform(value: unknown): value is AxiosPlatform {
|
export function isPlatform(value: any): value is AxiosPlatform {
|
||||||
return (
|
return (
|
||||||
isPlainObject(value) &&
|
isPlainObject(value) &&
|
||||||
isFunction(value.request) &&
|
isFunction(value.request) &&
|
||||||
|
|
|
@ -57,13 +57,8 @@ function createInstance(defaults: AxiosRequestConfig) {
|
||||||
const context = new Axios(defaults);
|
const context = new Axios(defaults);
|
||||||
const instance = context.request as AxiosInstance;
|
const instance = context.request as AxiosInstance;
|
||||||
|
|
||||||
Object.assign(instance, context, {
|
Object.assign(instance, context);
|
||||||
// instance.fork 内部调用了 context 的私有方法
|
Object.setPrototypeOf(instance, Axios.prototype);
|
||||||
// 所以直接调用 instance.fork 会导致程序抛出无法访问 context 私有方法的异常
|
|
||||||
// instance.fork 调用时 this 重新指向 context,解决此问题
|
|
||||||
fork: context.fork.bind(context),
|
|
||||||
});
|
|
||||||
Object.setPrototypeOf(instance, Object.getPrototypeOf(context));
|
|
||||||
|
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,12 +11,13 @@ import {
|
||||||
AxiosAdapterResponseError,
|
AxiosAdapterResponseError,
|
||||||
AxiosAdapterResponseData,
|
AxiosAdapterResponseData,
|
||||||
} from '../adapter';
|
} from '../adapter';
|
||||||
|
import InterceptorManager, { Interceptor } from './InterceptorManager';
|
||||||
import { mergeConfig } from './mergeConfig';
|
import { mergeConfig } from './mergeConfig';
|
||||||
import { CancelToken } from './cancel';
|
import { CancelToken } from './cancel';
|
||||||
import { dispatchRequest } from './dispatchRequest';
|
import { dispatchRequest } from './dispatchRequest';
|
||||||
import { AxiosTransformer } from './transformData';
|
import { AxiosTransformer } from './transformData';
|
||||||
|
|
||||||
import AxiosDomain from './AxiosDomain';
|
import AxiosDomain from './AxiosDomain';
|
||||||
import InterceptorManager from './InterceptorManager';
|
|
||||||
|
|
||||||
export type AxiosRequestMethod =
|
export type AxiosRequestMethod =
|
||||||
| AxiosAdapterRequestMethod
|
| AxiosAdapterRequestMethod
|
||||||
|
@ -243,26 +244,36 @@ export default class Axios extends AxiosDomain {
|
||||||
/**
|
/**
|
||||||
* 派生领域
|
* 派生领域
|
||||||
*/
|
*/
|
||||||
fork(defaults: AxiosRequestConfig = {}) {
|
fork = (defaults: AxiosRequestConfig = {}) => {
|
||||||
if (isString(defaults.baseURL) && !isAbsoluteURL(defaults.baseURL)) {
|
if (isString(defaults.baseURL) && !isAbsoluteURL(defaults.baseURL)) {
|
||||||
defaults.baseURL = combineURL(this.defaults.baseURL, defaults.baseURL);
|
defaults.baseURL = combineURL(this.defaults.baseURL, defaults.baseURL);
|
||||||
}
|
}
|
||||||
return new AxiosDomain(mergeConfig(this.defaults, defaults), (config) =>
|
return new AxiosDomain(mergeConfig(this.defaults, defaults), (config) =>
|
||||||
this.#processRequest(config),
|
this.#processRequest(config),
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
#processRequest(config: AxiosRequestConfig) {
|
#processRequest(config: AxiosRequestConfig) {
|
||||||
const { request, response } = this.interceptors;
|
const chain: [
|
||||||
|
Interceptor<AxiosRequestConfig> | Interceptor<AxiosResponse>,
|
||||||
|
] = [
|
||||||
|
{
|
||||||
|
resolved: dispatchRequest,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
let promiseRequest = Promise.resolve(config);
|
this.interceptors.request.forEach(chain.unshift.bind(chain));
|
||||||
request.forEach(({ resolved, rejected }) => {
|
this.interceptors.response.forEach(chain.push.bind(chain));
|
||||||
promiseRequest = promiseRequest.then(resolved, rejected);
|
|
||||||
}, true);
|
let next = Promise.resolve(config);
|
||||||
let promiseResponse = promiseRequest.then(dispatchRequest);
|
for (const { resolved, rejected } of chain) {
|
||||||
response.forEach(({ resolved, rejected }) => {
|
next = next.then(
|
||||||
promiseResponse = promiseResponse.then(resolved, rejected);
|
// @ts-ignore
|
||||||
});
|
resolved,
|
||||||
return promiseResponse;
|
rejected,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return next as Promise<AxiosResponse>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -167,38 +167,36 @@ export default class AxiosDomain {
|
||||||
|
|
||||||
return processRequest(mergeConfig(this.defaults, config));
|
return processRequest(mergeConfig(this.defaults, config));
|
||||||
};
|
};
|
||||||
|
|
||||||
this.#createAsRequests();
|
|
||||||
this.#createAspRequests();
|
|
||||||
this.#createAsdRequests();
|
|
||||||
}
|
|
||||||
|
|
||||||
#createAsRequests() {
|
|
||||||
for (const alias of AxiosDomain.as) {
|
|
||||||
this[alias] = function processAsRequest(url, config = {}) {
|
|
||||||
config.method = alias;
|
|
||||||
return this.request(url, config);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#createAspRequests() {
|
|
||||||
for (const alias of AxiosDomain.asp) {
|
|
||||||
this[alias] = function processAspRequest(url, params = {}, config = {}) {
|
|
||||||
config.method = alias;
|
|
||||||
config.params = deepMerge(params, config.params ?? {});
|
|
||||||
return this.request(url, config);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#createAsdRequests() {
|
|
||||||
for (const alias of AxiosDomain.asd) {
|
|
||||||
this[alias] = function processAsdRequest(url, data = {}, config = {}) {
|
|
||||||
config.method = alias;
|
|
||||||
config.data = deepMerge(data, config.data ?? {});
|
|
||||||
return this.request(url, config);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const alias of AxiosDomain.as) {
|
||||||
|
AxiosDomain.prototype[alias] = function processAsRequest(url, config = {}) {
|
||||||
|
config.method = alias;
|
||||||
|
return this.request(url, config);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const alias of AxiosDomain.asp) {
|
||||||
|
AxiosDomain.prototype[alias] = function processAspRequest(
|
||||||
|
url,
|
||||||
|
params = {},
|
||||||
|
config = {},
|
||||||
|
) {
|
||||||
|
config.method = alias;
|
||||||
|
config.params = deepMerge(params, config.params ?? {});
|
||||||
|
return this.request(url, config);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const alias of AxiosDomain.asd) {
|
||||||
|
AxiosDomain.prototype[alias] = function processAsdRequest(
|
||||||
|
url,
|
||||||
|
data = {},
|
||||||
|
config = {},
|
||||||
|
) {
|
||||||
|
config.method = alias;
|
||||||
|
config.data = deepMerge(data, config.data ?? {});
|
||||||
|
return this.request(url, config);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
@ -18,27 +18,33 @@ export interface InterceptorExecutor<T = unknown> {
|
||||||
export default class InterceptorManager<T = unknown> {
|
export default class InterceptorManager<T = unknown> {
|
||||||
#id = 0;
|
#id = 0;
|
||||||
|
|
||||||
#interceptors: AnyObject<Interceptor<T>> = {};
|
#interceptors = new Map<number, Interceptor<T>>();
|
||||||
|
|
||||||
|
get size() {
|
||||||
|
return this.#interceptors.size;
|
||||||
|
}
|
||||||
|
|
||||||
use(
|
use(
|
||||||
resolved: InterceptorResolved<T>,
|
resolved: InterceptorResolved<T>,
|
||||||
rejected?: InterceptorRejected<T>,
|
rejected?: InterceptorRejected<T>,
|
||||||
): number {
|
): number {
|
||||||
this.#interceptors[++this.#id] = {
|
this.#interceptors.set(++this.#id, {
|
||||||
resolved,
|
resolved,
|
||||||
rejected,
|
rejected,
|
||||||
};
|
});
|
||||||
|
|
||||||
return this.#id;
|
return this.#id;
|
||||||
}
|
}
|
||||||
|
|
||||||
eject(id: number): void {
|
eject(id: number): boolean {
|
||||||
delete this.#interceptors[id];
|
return this.#interceptors.delete(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
forEach(executor: InterceptorExecutor<T>, reverse?: boolean): void {
|
clear() {
|
||||||
let interceptors: Interceptor<T>[] = Object.values(this.#interceptors);
|
this.#interceptors.clear();
|
||||||
if (reverse) interceptors = interceptors.reverse();
|
}
|
||||||
interceptors.forEach(executor);
|
|
||||||
|
forEach(executor: InterceptorExecutor<T>): void {
|
||||||
|
this.#interceptors.forEach(executor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,8 +26,8 @@ export function mergeConfig(
|
||||||
);
|
);
|
||||||
|
|
||||||
for (const key of keysSet) {
|
for (const key of keysSet) {
|
||||||
const val1 = config1[key] as any;
|
const val1 = config1[key];
|
||||||
const val2 = config2[key] as any;
|
const val2 = config2[key];
|
||||||
|
|
||||||
// 只从 config2 中取值
|
// 只从 config2 中取值
|
||||||
if (fromConfig2Map[key]) {
|
if (fromConfig2Map[key]) {
|
||||||
|
|
|
@ -14,7 +14,7 @@ export function isString(value: any): value is string {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isPlainObject(value: any): value is object & AnyObject {
|
export function isPlainObject<T extends AnyObject>(value: any): value is T {
|
||||||
return _toString.call(value) === '[object Object]';
|
return _toString.call(value) === '[object Object]';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,8 +16,12 @@ describe('src/core/InterceptorManager.ts', () => {
|
||||||
const rej = vi.fn();
|
const rej = vi.fn();
|
||||||
const cb = vi.fn();
|
const cb = vi.fn();
|
||||||
|
|
||||||
|
expect(i.size).toBe(0);
|
||||||
|
|
||||||
const id = i.use(res, rej);
|
const id = i.use(res, rej);
|
||||||
|
|
||||||
|
expect(i.size).toBe(1);
|
||||||
|
|
||||||
i.forEach(({ resolved, rejected }) => {
|
i.forEach(({ resolved, rejected }) => {
|
||||||
expect(resolved).toBe(res);
|
expect(resolved).toBe(res);
|
||||||
expect(rejected).toBe(rej);
|
expect(rejected).toBe(rej);
|
||||||
|
@ -26,10 +30,31 @@ describe('src/core/InterceptorManager.ts', () => {
|
||||||
i.eject(id);
|
i.eject(id);
|
||||||
i.forEach(cb);
|
i.forEach(cb);
|
||||||
|
|
||||||
|
expect(i.size).toBe(0);
|
||||||
|
|
||||||
expect(cb).not.toBeCalled();
|
expect(cb).not.toBeCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('应该可以依次执行拦截处理函数', () => {
|
test('应该可以清理所有拦截处理函数', () => {
|
||||||
|
const i = new InterceptorManager();
|
||||||
|
const res = vi.fn();
|
||||||
|
const rej = vi.fn();
|
||||||
|
const cb = vi.fn();
|
||||||
|
|
||||||
|
expect(i.size).toBe(0);
|
||||||
|
|
||||||
|
i.use(res, rej);
|
||||||
|
i.use(res, rej);
|
||||||
|
i.use(res, rej);
|
||||||
|
|
||||||
|
expect(i.size).toBe(3);
|
||||||
|
|
||||||
|
i.clear();
|
||||||
|
|
||||||
|
expect(i.size).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('应该可以调用 forEach', () => {
|
||||||
const i = new InterceptorManager();
|
const i = new InterceptorManager();
|
||||||
const res1 = vi.fn();
|
const res1 = vi.fn();
|
||||||
const rej1 = vi.fn();
|
const rej1 = vi.fn();
|
||||||
|
@ -50,26 +75,4 @@ describe('src/core/InterceptorManager.ts', () => {
|
||||||
rejected: rej2,
|
rejected: rej2,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('应该可以反向依次执行拦截处理函数', () => {
|
|
||||||
const i = new InterceptorManager();
|
|
||||||
const res1 = vi.fn();
|
|
||||||
const rej1 = vi.fn();
|
|
||||||
const res2 = vi.fn();
|
|
||||||
const rej2 = vi.fn();
|
|
||||||
const cb = vi.fn();
|
|
||||||
|
|
||||||
i.use(res1, rej1);
|
|
||||||
i.use(res2, rej2);
|
|
||||||
i.forEach(cb, true);
|
|
||||||
|
|
||||||
expect(cb.mock.calls[0][0]).toEqual({
|
|
||||||
resolved: res2,
|
|
||||||
rejected: rej2,
|
|
||||||
});
|
|
||||||
expect(cb.mock.calls[1][0]).toEqual({
|
|
||||||
resolved: res1,
|
|
||||||
rejected: rej1,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue