feat: 支持复用父级中间件
parent
a84533a09f
commit
bfc012b499
|
@ -4,9 +4,8 @@ import {
|
||||||
isCancel,
|
isCancel,
|
||||||
} from './request/cancel';
|
} from './request/cancel';
|
||||||
import { isAxiosError } from './request/createError';
|
import { isAxiosError } from './request/createError';
|
||||||
import Axios, { AxiosConstructor, AxiosRequestConfig } from './core/Axios';
|
import Axios, { AxiosConstructor } from './core/Axios';
|
||||||
import { AxiosInstance, createInstance } from './core/createInstance';
|
import { AxiosInstance, createInstance } from './core/createInstance';
|
||||||
import { mergeConfig } from './core/mergeConfig';
|
|
||||||
import { createAdapter } from './adpater/createAdapter';
|
import { createAdapter } from './adpater/createAdapter';
|
||||||
import defaults from './defaults';
|
import defaults from './defaults';
|
||||||
import { version } from './version';
|
import { version } from './version';
|
||||||
|
|
|
@ -21,7 +21,10 @@ import {
|
||||||
WITH_DATA_METHODS,
|
WITH_DATA_METHODS,
|
||||||
WITH_PARAMS_METHODS,
|
WITH_PARAMS_METHODS,
|
||||||
} from '../constants/methods';
|
} from '../constants/methods';
|
||||||
import MiddlewareManager from './MiddlewareManager';
|
import MiddlewareManager, {
|
||||||
|
MiddlewareNext,
|
||||||
|
MiddlewareUse,
|
||||||
|
} from './MiddlewareManager';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 请求方法
|
* 请求方法
|
||||||
|
@ -379,9 +382,7 @@ export default class Axios {
|
||||||
/**
|
/**
|
||||||
* 中间件
|
* 中间件
|
||||||
*/
|
*/
|
||||||
#middleware = new MiddlewareManager<AxiosContext>(async (ctx) => {
|
#middleware = new MiddlewareManager<AxiosContext>();
|
||||||
ctx.res = await dispatchRequest(ctx.req);
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 发送 options 请求
|
* 发送 options 请求
|
||||||
|
@ -431,17 +432,12 @@ export default class Axios {
|
||||||
/**
|
/**
|
||||||
* 添加中间件
|
* 添加中间件
|
||||||
*/
|
*/
|
||||||
use: MiddlewareManager<AxiosContext>['use'];
|
use: MiddlewareUse<AxiosContext>;
|
||||||
|
|
||||||
constructor(defaults: AxiosRequestConfig = {}, parent?: Axios) {
|
constructor(defaults: AxiosRequestConfig = {}, parent?: Axios) {
|
||||||
this.defaults = defaults;
|
this.defaults = defaults;
|
||||||
this.#parent = parent;
|
this.#parent = parent;
|
||||||
if (this.#parent) {
|
this.use = this.#middleware.use;
|
||||||
this.#middleware.flush = this.#parent.#middleware.wrap(
|
|
||||||
this.#middleware.flush,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
this.use = this.#middleware.use.bind(this.#middleware);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -463,15 +459,7 @@ export default class Axios {
|
||||||
|
|
||||||
#processRequest(config: AxiosRequestConfig) {
|
#processRequest(config: AxiosRequestConfig) {
|
||||||
const requestHandler = {
|
const requestHandler = {
|
||||||
resolved: async (config: AxiosRequestConfig) => {
|
resolved: this.#requestHandler,
|
||||||
config.url = combineURL(config.baseURL, config.url);
|
|
||||||
const ctx: AxiosContext = {
|
|
||||||
req: config,
|
|
||||||
res: null,
|
|
||||||
};
|
|
||||||
await this.#middleware.flush(ctx);
|
|
||||||
return ctx.res as AxiosResponse;
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
const errorHandler = {
|
const errorHandler = {
|
||||||
rejected: config.errorHandler,
|
rejected: config.errorHandler,
|
||||||
|
@ -481,11 +469,11 @@ export default class Axios {
|
||||||
| Partial<Interceptor<AxiosResponse>>
|
| Partial<Interceptor<AxiosResponse>>
|
||||||
)[] = [];
|
)[] = [];
|
||||||
|
|
||||||
this.#eacheRequestInterceptors((requestInterceptor) => {
|
this.#eachRequestInterceptors((requestInterceptor) => {
|
||||||
chain.unshift(requestInterceptor);
|
chain.unshift(requestInterceptor);
|
||||||
});
|
});
|
||||||
chain.push(requestHandler);
|
chain.push(requestHandler);
|
||||||
this.#eacheResponseInterceptors((responseInterceptor) => {
|
this.#eachResponseInterceptors((responseInterceptor) => {
|
||||||
chain.push(responseInterceptor);
|
chain.push(responseInterceptor);
|
||||||
});
|
});
|
||||||
chain.push(errorHandler);
|
chain.push(errorHandler);
|
||||||
|
@ -501,19 +489,40 @@ export default class Axios {
|
||||||
) as Promise<AxiosResponse>;
|
) as Promise<AxiosResponse>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#eacheRequestInterceptors(executor: InterceptorExecutor<AxiosRequestConfig>) {
|
#eachRequestInterceptors(executor: InterceptorExecutor<AxiosRequestConfig>) {
|
||||||
this.interceptors.request.forEach(executor);
|
this.interceptors.request.forEach(executor);
|
||||||
if (this.#parent) {
|
if (this.#parent) {
|
||||||
this.#parent.#eacheRequestInterceptors(executor);
|
this.#parent.#eachRequestInterceptors(executor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#eacheResponseInterceptors(executor: InterceptorExecutor<AxiosResponse>) {
|
#eachResponseInterceptors(executor: InterceptorExecutor<AxiosResponse>) {
|
||||||
this.interceptors.response.forEach(executor);
|
this.interceptors.response.forEach(executor);
|
||||||
if (this.#parent) {
|
if (this.#parent) {
|
||||||
this.#parent.#eacheResponseInterceptors(executor);
|
this.#parent.#eachResponseInterceptors(executor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#requestHandler = async (config: AxiosRequestConfig) => {
|
||||||
|
config.url = combineURL(config.baseURL, config.url);
|
||||||
|
const ctx: AxiosContext = {
|
||||||
|
req: config,
|
||||||
|
res: null,
|
||||||
|
};
|
||||||
|
await this.#flush(ctx, async () => {
|
||||||
|
ctx.res = await dispatchRequest(ctx.req);
|
||||||
|
});
|
||||||
|
return ctx.res as AxiosResponse;
|
||||||
|
};
|
||||||
|
|
||||||
|
#flush(ctx: AxiosContext, finish: MiddlewareNext): Promise<void> {
|
||||||
|
if (this.#parent) {
|
||||||
|
return this.#parent.#flush(ctx, () => {
|
||||||
|
return this.#middleware.flush(ctx, finish);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return this.#middleware.flush(ctx, finish);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const method of PLAIN_METHODS) {
|
for (const method of PLAIN_METHODS) {
|
||||||
|
|
|
@ -6,32 +6,39 @@ export interface MiddlewareNext {
|
||||||
(): Promise<void>;
|
(): Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MiddlewareCallback<Conext extends AnyObject> {
|
export interface MiddlewareCallback<Context extends AnyObject> {
|
||||||
(ctx: Conext, next: MiddlewareNext): Promise<void>;
|
(ctx: Context, next: MiddlewareNext): Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MiddlewareFlush<Conext extends AnyObject> {
|
export interface MiddlewareUse<Context extends AnyObject> {
|
||||||
(ctx: Conext): Promise<void>;
|
/**
|
||||||
}
|
* 添加中间件
|
||||||
|
*
|
||||||
export default class MiddlewareManager<Conext extends AnyObject = AnyObject> {
|
* @param path 中间件路径
|
||||||
#map = new Map<string, MiddlewareCallback<Conext>[]>();
|
* @param callback 中间件回调
|
||||||
|
*/
|
||||||
flush: MiddlewareFlush<Conext>;
|
(
|
||||||
|
|
||||||
constructor(flush: MiddlewareFlush<Conext>) {
|
|
||||||
this.flush = this.wrap(flush);
|
|
||||||
}
|
|
||||||
|
|
||||||
use(callback: MiddlewareCallback<Conext>): MiddlewareManager<Conext>;
|
|
||||||
use(
|
|
||||||
path: string,
|
path: string,
|
||||||
callback: MiddlewareCallback<Conext>,
|
callback: MiddlewareCallback<Context>,
|
||||||
): MiddlewareManager<Conext>;
|
): MiddlewareManager<Context>;
|
||||||
use(
|
/**
|
||||||
path: string | MiddlewareCallback<Conext>,
|
* 添加中间件
|
||||||
callback?: MiddlewareCallback<Conext>,
|
*
|
||||||
) {
|
* @param callback 中间件回调
|
||||||
|
*/
|
||||||
|
(callback: MiddlewareCallback<Context>): MiddlewareManager<Context>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class MiddlewareManager<Context extends AnyObject = AnyObject> {
|
||||||
|
#map = new Map<string, MiddlewareCallback<Context>[]>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加中间件
|
||||||
|
*/
|
||||||
|
use: MiddlewareUse<Context> = (
|
||||||
|
path: string | MiddlewareCallback<Context>,
|
||||||
|
callback?: MiddlewareCallback<Context>,
|
||||||
|
) => {
|
||||||
if (isFunction(path)) {
|
if (isFunction(path)) {
|
||||||
callback = path;
|
callback = path;
|
||||||
path = '/';
|
path = '/';
|
||||||
|
@ -45,11 +52,10 @@ export default class MiddlewareManager<Conext extends AnyObject = AnyObject> {
|
||||||
this.#map.set(path, middlewares);
|
this.#map.set(path, middlewares);
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
};
|
||||||
|
|
||||||
wrap(flush: MiddlewareFlush<Conext>): MiddlewareFlush<Conext> {
|
flush(ctx: Context, finish: MiddlewareNext) {
|
||||||
return (ctx) => {
|
const allMiddlewares: MiddlewareCallback<Context>[] = [];
|
||||||
const allMiddlewares: MiddlewareCallback<Conext>[] = [];
|
|
||||||
|
|
||||||
for (const [path, middlewares] of this.#map.entries()) {
|
for (const [path, middlewares] of this.#map.entries()) {
|
||||||
const url = combineURL(ctx.req.baseURL, path);
|
const url = combineURL(ctx.req.baseURL, path);
|
||||||
|
@ -62,10 +68,9 @@ export default class MiddlewareManager<Conext extends AnyObject = AnyObject> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const tasks = [...allMiddlewares, flush];
|
const tasks = [...allMiddlewares, finish];
|
||||||
return (function next(): Promise<void> {
|
return (function next(): Promise<void> {
|
||||||
return tasks.shift()!(ctx, next);
|
return tasks.shift()!(ctx, next);
|
||||||
})();
|
})();
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,7 +68,7 @@ export function createInstance(config: AxiosRequestConfig, parent?: Axios) {
|
||||||
return createInstance(mergeConfig(instance.defaults, config));
|
return createInstance(mergeConfig(instance.defaults, config));
|
||||||
};
|
};
|
||||||
instance.extend = function extend(config: AxiosRequestConfig = {}) {
|
instance.extend = function extend(config: AxiosRequestConfig = {}) {
|
||||||
config.url = combineURL(instance.defaults.baseURL, config.url);
|
config.baseURL = combineURL(instance.defaults.baseURL, config.baseURL);
|
||||||
return createInstance(mergeConfig(instance.defaults, config), context);
|
return createInstance(mergeConfig(instance.defaults, config), context);
|
||||||
};
|
};
|
||||||
instance.fork = instance.extend;
|
instance.fork = instance.extend;
|
||||||
|
|
|
@ -1,79 +0,0 @@
|
||||||
import { describe, test, expect, vi } from 'vitest';
|
|
||||||
import MiddlewareManager from '@/core/MiddlewareManager';
|
|
||||||
|
|
||||||
describe('src/core/MiddlewareManager.ts', () => {
|
|
||||||
test('应该有这些实例属性', () => {
|
|
||||||
const m = new MiddlewareManager(vi.fn());
|
|
||||||
|
|
||||||
expect(m.use).toBeTypeOf('function');
|
|
||||||
expect(m.wrap).toBeTypeOf('function');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('应该可以添加中间件回调', async () => {
|
|
||||||
const flush = vi.fn(async (ctx) => {
|
|
||||||
expect(ctx.req.url).toBe('test');
|
|
||||||
ctx.res = res;
|
|
||||||
});
|
|
||||||
const m = new MiddlewareManager(flush);
|
|
||||||
const ctx = {
|
|
||||||
req: { url: 'https://api.com' },
|
|
||||||
res: null,
|
|
||||||
};
|
|
||||||
const res = {
|
|
||||||
'src/core/MiddlewareManager.ts': true,
|
|
||||||
};
|
|
||||||
const midde = vi.fn(async (ctx, next) => {
|
|
||||||
expect(ctx).toBe(ctx);
|
|
||||||
ctx.req.url = 'test';
|
|
||||||
await next();
|
|
||||||
expect(ctx.res).toBe(res);
|
|
||||||
});
|
|
||||||
|
|
||||||
m.use(midde);
|
|
||||||
await m.flush(ctx);
|
|
||||||
|
|
||||||
expect(ctx.res).toBe(res);
|
|
||||||
expect(midde).toBeCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('应该可以给路径添加中间件回调', async () => {
|
|
||||||
const flush = vi.fn(async (ctx) => {
|
|
||||||
ctx.res = res;
|
|
||||||
});
|
|
||||||
|
|
||||||
const m = new MiddlewareManager(flush);
|
|
||||||
const ctx1 = {
|
|
||||||
req: {
|
|
||||||
baseURL: 'https://api.com',
|
|
||||||
url: 'https://api.com',
|
|
||||||
},
|
|
||||||
res: null,
|
|
||||||
};
|
|
||||||
const ctx2 = {
|
|
||||||
req: {
|
|
||||||
baseURL: 'https://api.com',
|
|
||||||
url: 'https://api.com/test',
|
|
||||||
},
|
|
||||||
res: null,
|
|
||||||
};
|
|
||||||
const res = {
|
|
||||||
'src/core/MiddlewareManager.ts': true,
|
|
||||||
};
|
|
||||||
const midde = vi.fn(async (ctx, next) => {
|
|
||||||
expect(ctx).toBe(ctx);
|
|
||||||
await next();
|
|
||||||
expect(ctx.res).toBe(res);
|
|
||||||
});
|
|
||||||
|
|
||||||
m.use('/test', midde);
|
|
||||||
await m.flush(ctx1);
|
|
||||||
|
|
||||||
expect(ctx1.res).toBe(res);
|
|
||||||
expect(midde).not.toBeCalled();
|
|
||||||
|
|
||||||
m.use('/test', midde);
|
|
||||||
await m.flush(ctx2);
|
|
||||||
|
|
||||||
expect(midde).toBeCalled();
|
|
||||||
});
|
|
||||||
});
|
|
Loading…
Reference in New Issue