feat: 支持复用父级中间件
parent
a84533a09f
commit
bfc012b499
|
@ -4,9 +4,8 @@ import {
|
|||
isCancel,
|
||||
} from './request/cancel';
|
||||
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 { mergeConfig } from './core/mergeConfig';
|
||||
import { createAdapter } from './adpater/createAdapter';
|
||||
import defaults from './defaults';
|
||||
import { version } from './version';
|
||||
|
|
|
@ -21,7 +21,10 @@ import {
|
|||
WITH_DATA_METHODS,
|
||||
WITH_PARAMS_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) => {
|
||||
ctx.res = await dispatchRequest(ctx.req);
|
||||
});
|
||||
#middleware = new MiddlewareManager<AxiosContext>();
|
||||
|
||||
/**
|
||||
* 发送 options 请求
|
||||
|
@ -431,17 +432,12 @@ export default class Axios {
|
|||
/**
|
||||
* 添加中间件
|
||||
*/
|
||||
use: MiddlewareManager<AxiosContext>['use'];
|
||||
use: MiddlewareUse<AxiosContext>;
|
||||
|
||||
constructor(defaults: AxiosRequestConfig = {}, parent?: Axios) {
|
||||
this.defaults = defaults;
|
||||
this.#parent = parent;
|
||||
if (this.#parent) {
|
||||
this.#middleware.flush = this.#parent.#middleware.wrap(
|
||||
this.#middleware.flush,
|
||||
);
|
||||
}
|
||||
this.use = this.#middleware.use.bind(this.#middleware);
|
||||
this.use = this.#middleware.use;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -463,15 +459,7 @@ export default class Axios {
|
|||
|
||||
#processRequest(config: AxiosRequestConfig) {
|
||||
const requestHandler = {
|
||||
resolved: async (config: AxiosRequestConfig) => {
|
||||
config.url = combineURL(config.baseURL, config.url);
|
||||
const ctx: AxiosContext = {
|
||||
req: config,
|
||||
res: null,
|
||||
};
|
||||
await this.#middleware.flush(ctx);
|
||||
return ctx.res as AxiosResponse;
|
||||
},
|
||||
resolved: this.#requestHandler,
|
||||
};
|
||||
const errorHandler = {
|
||||
rejected: config.errorHandler,
|
||||
|
@ -481,11 +469,11 @@ export default class Axios {
|
|||
| Partial<Interceptor<AxiosResponse>>
|
||||
)[] = [];
|
||||
|
||||
this.#eacheRequestInterceptors((requestInterceptor) => {
|
||||
this.#eachRequestInterceptors((requestInterceptor) => {
|
||||
chain.unshift(requestInterceptor);
|
||||
});
|
||||
chain.push(requestHandler);
|
||||
this.#eacheResponseInterceptors((responseInterceptor) => {
|
||||
this.#eachResponseInterceptors((responseInterceptor) => {
|
||||
chain.push(responseInterceptor);
|
||||
});
|
||||
chain.push(errorHandler);
|
||||
|
@ -501,19 +489,40 @@ export default class Axios {
|
|||
) as Promise<AxiosResponse>;
|
||||
}
|
||||
|
||||
#eacheRequestInterceptors(executor: InterceptorExecutor<AxiosRequestConfig>) {
|
||||
#eachRequestInterceptors(executor: InterceptorExecutor<AxiosRequestConfig>) {
|
||||
this.interceptors.request.forEach(executor);
|
||||
if (this.#parent) {
|
||||
this.#parent.#eacheRequestInterceptors(executor);
|
||||
this.#parent.#eachRequestInterceptors(executor);
|
||||
}
|
||||
}
|
||||
|
||||
#eacheResponseInterceptors(executor: InterceptorExecutor<AxiosResponse>) {
|
||||
#eachResponseInterceptors(executor: InterceptorExecutor<AxiosResponse>) {
|
||||
this.interceptors.response.forEach(executor);
|
||||
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) {
|
||||
|
|
|
@ -6,32 +6,39 @@ export interface MiddlewareNext {
|
|||
(): Promise<void>;
|
||||
}
|
||||
|
||||
export interface MiddlewareCallback<Conext extends AnyObject> {
|
||||
(ctx: Conext, next: MiddlewareNext): Promise<void>;
|
||||
export interface MiddlewareCallback<Context extends AnyObject> {
|
||||
(ctx: Context, next: MiddlewareNext): Promise<void>;
|
||||
}
|
||||
|
||||
export interface MiddlewareFlush<Conext extends AnyObject> {
|
||||
(ctx: Conext): Promise<void>;
|
||||
}
|
||||
|
||||
export default class MiddlewareManager<Conext extends AnyObject = AnyObject> {
|
||||
#map = new Map<string, MiddlewareCallback<Conext>[]>();
|
||||
|
||||
flush: MiddlewareFlush<Conext>;
|
||||
|
||||
constructor(flush: MiddlewareFlush<Conext>) {
|
||||
this.flush = this.wrap(flush);
|
||||
}
|
||||
|
||||
use(callback: MiddlewareCallback<Conext>): MiddlewareManager<Conext>;
|
||||
use(
|
||||
export interface MiddlewareUse<Context extends AnyObject> {
|
||||
/**
|
||||
* 添加中间件
|
||||
*
|
||||
* @param path 中间件路径
|
||||
* @param callback 中间件回调
|
||||
*/
|
||||
(
|
||||
path: string,
|
||||
callback: MiddlewareCallback<Conext>,
|
||||
): MiddlewareManager<Conext>;
|
||||
use(
|
||||
path: string | MiddlewareCallback<Conext>,
|
||||
callback?: MiddlewareCallback<Conext>,
|
||||
) {
|
||||
callback: MiddlewareCallback<Context>,
|
||||
): MiddlewareManager<Context>;
|
||||
/**
|
||||
* 添加中间件
|
||||
*
|
||||
* @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)) {
|
||||
callback = path;
|
||||
path = '/';
|
||||
|
@ -45,27 +52,25 @@ export default class MiddlewareManager<Conext extends AnyObject = AnyObject> {
|
|||
this.#map.set(path, middlewares);
|
||||
|
||||
return this;
|
||||
}
|
||||
};
|
||||
|
||||
wrap(flush: MiddlewareFlush<Conext>): MiddlewareFlush<Conext> {
|
||||
return (ctx) => {
|
||||
const allMiddlewares: MiddlewareCallback<Conext>[] = [];
|
||||
flush(ctx: Context, finish: MiddlewareNext) {
|
||||
const allMiddlewares: MiddlewareCallback<Context>[] = [];
|
||||
|
||||
for (const [path, middlewares] of this.#map.entries()) {
|
||||
const url = combineURL(ctx.req.baseURL, path);
|
||||
const checkRE = new RegExp(`^${url}([/?].*)?`);
|
||||
for (const [path, middlewares] of this.#map.entries()) {
|
||||
const url = combineURL(ctx.req.baseURL, path);
|
||||
const checkRE = new RegExp(`^${url}([/?].*)?`);
|
||||
|
||||
if (path === '/') {
|
||||
allMiddlewares.push(...middlewares);
|
||||
} else if (checkRE.test(ctx.req.url!)) {
|
||||
allMiddlewares.push(...middlewares);
|
||||
}
|
||||
if (path === '/') {
|
||||
allMiddlewares.push(...middlewares);
|
||||
} else if (checkRE.test(ctx.req.url!)) {
|
||||
allMiddlewares.push(...middlewares);
|
||||
}
|
||||
}
|
||||
|
||||
const tasks = [...allMiddlewares, flush];
|
||||
return (function next(): Promise<void> {
|
||||
return tasks.shift()!(ctx, next);
|
||||
})();
|
||||
};
|
||||
const tasks = [...allMiddlewares, finish];
|
||||
return (function next(): Promise<void> {
|
||||
return tasks.shift()!(ctx, next);
|
||||
})();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -68,7 +68,7 @@ export function createInstance(config: AxiosRequestConfig, parent?: Axios) {
|
|||
return createInstance(mergeConfig(instance.defaults, config));
|
||||
};
|
||||
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);
|
||||
};
|
||||
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