parent
6263759ba9
commit
9093e1bdff
|
@ -140,11 +140,11 @@ export interface AxiosAdapterBaseOptions extends AxiosAdapterRequestConfig {
|
|||
/**
|
||||
* 成功的回调
|
||||
*/
|
||||
success(response: AxiosAdapterResponse): void;
|
||||
success(response: AnyObject): void;
|
||||
/**
|
||||
* 失败的回调
|
||||
*/
|
||||
fail(error: AxiosAdapterResponseError): void;
|
||||
fail(error: AnyObject): void;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -271,11 +271,11 @@ export function createAdapter(platform: AxiosAdapterPlatform) {
|
|||
return {
|
||||
...config,
|
||||
header: config.headers,
|
||||
success(response) {
|
||||
success(response: AxiosAdapterResponse) {
|
||||
transformResponse(response);
|
||||
config.success(response);
|
||||
},
|
||||
fail(responseError) {
|
||||
fail(responseError: AxiosAdapterResponseError) {
|
||||
responseError.data = {
|
||||
errMsg: responseError.errMsg,
|
||||
errno: responseError.errno,
|
||||
|
@ -284,6 +284,7 @@ export function createAdapter(platform: AxiosAdapterPlatform) {
|
|||
config.fail(responseError);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function transformResponse(
|
||||
response: AxiosAdapterResponse | AxiosAdapterResponseError,
|
||||
|
@ -292,7 +293,6 @@ export function createAdapter(platform: AxiosAdapterPlatform) {
|
|||
response.headers = response.headers ?? response.header;
|
||||
clean(response, ['statusCode', 'errMsg', 'errno', 'header']);
|
||||
}
|
||||
}
|
||||
|
||||
function processRequest(
|
||||
request: AxiosAdapterRequest,
|
||||
|
|
56
src/axios.ts
56
src/axios.ts
|
@ -4,37 +4,13 @@ import {
|
|||
isCancel,
|
||||
} from './request/cancel';
|
||||
import { isAxiosError } from './request/createError';
|
||||
import Axios, { AxiosConstructor, AxiosRequestConfig } from './core/Axios';
|
||||
import { AxiosInstance, createInstance } from './core/createInstance';
|
||||
import { mergeConfig } from './core/mergeConfig';
|
||||
import { AxiosDomainRequest } from './core/AxiosDomain';
|
||||
import Axios, {
|
||||
AxiosConstructor,
|
||||
AxiosRequestConfig,
|
||||
AxiosRequestHeaders,
|
||||
} from './core/Axios';
|
||||
import { createAdapter } from './adpater/createAdapter';
|
||||
import defaults from './defaults';
|
||||
import { version } from './version';
|
||||
|
||||
/**
|
||||
* axios 实例默认配置
|
||||
*/
|
||||
export interface AxiosInstanceDefaults extends AxiosRequestConfig {
|
||||
/**
|
||||
* 请求头
|
||||
*/
|
||||
headers: Required<AxiosRequestHeaders>;
|
||||
}
|
||||
|
||||
/**
|
||||
* axios 实例
|
||||
*/
|
||||
export interface AxiosInstance extends AxiosDomainRequest, Axios {
|
||||
/**
|
||||
* 默认请求配置
|
||||
*/
|
||||
defaults: AxiosInstanceDefaults;
|
||||
}
|
||||
|
||||
/**
|
||||
* axios 静态对象
|
||||
*/
|
||||
|
@ -51,12 +27,6 @@ export interface AxiosStatic extends AxiosInstance {
|
|||
* 取消令牌
|
||||
*/
|
||||
CancelToken: CancelTokenConstructor;
|
||||
/**
|
||||
* 创建 axios 实例
|
||||
*
|
||||
* @param config 默认配置
|
||||
*/
|
||||
create(config?: AxiosRequestConfig): AxiosInstance;
|
||||
/**
|
||||
* 创建适配器
|
||||
*/
|
||||
|
@ -69,26 +39,18 @@ export interface AxiosStatic extends AxiosInstance {
|
|||
* 传入响应错误返回 true
|
||||
*/
|
||||
isAxiosError: typeof isAxiosError;
|
||||
}
|
||||
|
||||
function createInstance(config: AxiosRequestConfig) {
|
||||
const context = new Axios(config);
|
||||
const instance = context.request as AxiosInstance;
|
||||
|
||||
Object.assign(instance, context);
|
||||
Object.setPrototypeOf(instance, Axios.prototype);
|
||||
|
||||
return instance;
|
||||
/**
|
||||
* 创建 axios 实例
|
||||
*
|
||||
* @param config 默认配置
|
||||
*/
|
||||
create(config?: AxiosRequestConfig): AxiosInstance;
|
||||
}
|
||||
|
||||
const axios = createInstance(defaults) as AxiosStatic;
|
||||
|
||||
axios.create = function create(config) {
|
||||
const instance = createInstance(mergeConfig(axios.defaults, config));
|
||||
instance.flush = axios.middleware.wrap(instance.flush);
|
||||
return instance;
|
||||
return createInstance(mergeConfig(axios.defaults, config));
|
||||
};
|
||||
|
||||
axios.version = version;
|
||||
axios.Axios = Axios;
|
||||
axios.CancelToken = CancelToken;
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
import { buildURL } from '../helpers/buildURL';
|
||||
import { combineURL } from '../helpers/combineURL';
|
||||
import { isString } from '../helpers/isTypes';
|
||||
import { dispatchRequest } from '../request/dispatchRequest';
|
||||
import { CancelToken } from '../request/cancel';
|
||||
import { AxiosTransformer } from '../request/transformData';
|
||||
import { deepMerge } from '../helpers/deepMerge';
|
||||
import {
|
||||
AxiosAdapter,
|
||||
AxiosAdapterRequestMethod,
|
||||
|
@ -9,9 +11,17 @@ import {
|
|||
AxiosAdapterRequestConfig,
|
||||
AxiosAdapterResponseData,
|
||||
} from '../adpater/createAdapter';
|
||||
import InterceptorManager, { Interceptor } from './InterceptorManager';
|
||||
import InterceptorManager, {
|
||||
Interceptor,
|
||||
InterceptorExecutor,
|
||||
} from './InterceptorManager';
|
||||
import { mergeConfig } from './mergeConfig';
|
||||
import AxiosDomain, { AxiosDomainRequestHandler } from './AxiosDomain';
|
||||
import {
|
||||
PLAIN_METHODS,
|
||||
WITH_DATA_METHODS,
|
||||
WITH_PARAMS_METHODS,
|
||||
} from '../constants/methods';
|
||||
import MiddlewareManager from './MiddlewareManager';
|
||||
|
||||
/**
|
||||
* 请求方法
|
||||
|
@ -293,6 +303,51 @@ export interface AxiosResponseError extends AnyObject {
|
|||
request?: AxiosAdapterPlatformTask;
|
||||
}
|
||||
|
||||
export interface AxiosContext {
|
||||
req: AxiosRequestConfig;
|
||||
res: null | AxiosResponse;
|
||||
}
|
||||
|
||||
export interface AxiosRequest {
|
||||
<TData extends AxiosResponseData>(config: AxiosRequestConfig): Promise<
|
||||
AxiosResponse<TData>
|
||||
>;
|
||||
<TData extends AxiosResponseData>(
|
||||
url: string,
|
||||
config?: AxiosRequestConfig,
|
||||
): Promise<AxiosResponse<TData>>;
|
||||
}
|
||||
|
||||
/**
|
||||
* 普通的请求方法
|
||||
*/
|
||||
export type AxiosRequestMethodFn = <TData extends AxiosResponseData>(
|
||||
url: string,
|
||||
config?: AxiosRequestConfig,
|
||||
) => Promise<AxiosResponse<TData>>;
|
||||
|
||||
/**
|
||||
* 带参数的请求方法
|
||||
*/
|
||||
export type AxiosRequestMethodFnWithParams = <TData extends AxiosResponseData>(
|
||||
url: string,
|
||||
params?: AnyObject,
|
||||
config?: AxiosRequestConfig,
|
||||
) => Promise<AxiosResponse<TData>>;
|
||||
|
||||
/**
|
||||
* 带数据的请求方法
|
||||
*/
|
||||
export type AxiosRequestMethodFnWithData = <TData extends AxiosResponseData>(
|
||||
url: string,
|
||||
data?: AxiosRequestData,
|
||||
config?: AxiosRequestConfig,
|
||||
) => Promise<AxiosResponse<TData>>;
|
||||
|
||||
export interface AxiosDomainRequestHandler {
|
||||
(config: AxiosRequestConfig): Promise<AxiosResponse>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Axios 构造函数
|
||||
*/
|
||||
|
@ -300,7 +355,13 @@ export interface AxiosConstructor {
|
|||
new (config: AxiosRequestConfig): Axios;
|
||||
}
|
||||
|
||||
export default class Axios extends AxiosDomain {
|
||||
export default class Axios {
|
||||
#parent?: Axios;
|
||||
/**
|
||||
* 默认请求配置
|
||||
*/
|
||||
defaults: AxiosRequestConfig;
|
||||
|
||||
/**
|
||||
* 拦截器
|
||||
*/
|
||||
|
@ -315,37 +376,102 @@ export default class Axios extends AxiosDomain {
|
|||
response: new InterceptorManager<AxiosResponse>(),
|
||||
};
|
||||
|
||||
constructor(defaults: AxiosRequestConfig = {}) {
|
||||
super(defaults, (...args) => this.#processRequest(...args));
|
||||
}
|
||||
/**
|
||||
* 中间件
|
||||
*/
|
||||
#middleware = new MiddlewareManager<AxiosContext>(async (ctx) => {
|
||||
ctx.res = await dispatchRequest(ctx.req);
|
||||
});
|
||||
|
||||
getUri(config: AxiosRequestConfig): string {
|
||||
const { url, params, paramsSerializer } = mergeConfig(
|
||||
this.defaults,
|
||||
config,
|
||||
/**
|
||||
* 发送 options 请求
|
||||
*/
|
||||
options!: AxiosRequestMethodFn;
|
||||
|
||||
/**
|
||||
* 发送 get 请求
|
||||
*/
|
||||
get!: AxiosRequestMethodFnWithParams;
|
||||
|
||||
/**
|
||||
* 发送 head 请求
|
||||
*/
|
||||
head!: AxiosRequestMethodFnWithParams;
|
||||
|
||||
/**
|
||||
* 发送 post 请求
|
||||
*/
|
||||
post!: AxiosRequestMethodFnWithData;
|
||||
|
||||
/**
|
||||
* 发送 put 请求
|
||||
*/
|
||||
put!: AxiosRequestMethodFnWithData;
|
||||
|
||||
/**
|
||||
* 发送 patch 请求
|
||||
*/
|
||||
patch!: AxiosRequestMethodFnWithData;
|
||||
|
||||
/**
|
||||
* 发送 delete 请求
|
||||
*/
|
||||
delete!: AxiosRequestMethodFnWithParams;
|
||||
|
||||
/**
|
||||
* 发送 trace 请求
|
||||
*/
|
||||
trace!: AxiosRequestMethodFn;
|
||||
|
||||
/**
|
||||
* 发送 connect 请求
|
||||
*/
|
||||
connect!: AxiosRequestMethodFn;
|
||||
|
||||
/**
|
||||
* 添加中间件
|
||||
*/
|
||||
use: MiddlewareManager<AxiosContext>['use'];
|
||||
|
||||
constructor(defaults: AxiosRequestConfig = {}, parent?: Axios) {
|
||||
this.defaults = defaults;
|
||||
this.#parent = parent;
|
||||
if (this.#parent) {
|
||||
this.#middleware.flush = this.#parent.#middleware.wrap(
|
||||
this.#middleware.flush,
|
||||
);
|
||||
return buildURL(url, params, paramsSerializer).replace(/^\?/, '');
|
||||
}
|
||||
this.use = this.#middleware.use.bind(this.#middleware);
|
||||
}
|
||||
|
||||
/**
|
||||
* 派生领域
|
||||
* 发送请求
|
||||
*/
|
||||
fork = (config: AxiosRequestConfig = {}) => {
|
||||
config.baseURL = combineURL(this.defaults.baseURL, config.baseURL);
|
||||
const domain = new AxiosDomain(
|
||||
mergeConfig(this.defaults, config),
|
||||
(...args) => this.#processRequest(...args),
|
||||
);
|
||||
domain.flush = this.middleware.wrap(domain.flush);
|
||||
return domain;
|
||||
request: AxiosRequest = (
|
||||
urlOrConfig: string | AxiosRequestConfig,
|
||||
config: AxiosRequestConfig = {},
|
||||
) => {
|
||||
if (isString(urlOrConfig)) {
|
||||
config.url = urlOrConfig;
|
||||
} else {
|
||||
config = urlOrConfig;
|
||||
}
|
||||
config.method = config.method || 'get';
|
||||
|
||||
return this.#processRequest(mergeConfig(this.defaults, config));
|
||||
};
|
||||
|
||||
#processRequest(
|
||||
config: AxiosRequestConfig,
|
||||
requestHandlerFn: AxiosDomainRequestHandler,
|
||||
) {
|
||||
#processRequest(config: AxiosRequestConfig) {
|
||||
const requestHandler = {
|
||||
resolved: requestHandlerFn,
|
||||
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;
|
||||
},
|
||||
};
|
||||
const errorHandler = {
|
||||
rejected: config.errorHandler,
|
||||
|
@ -355,11 +481,11 @@ export default class Axios extends AxiosDomain {
|
|||
| Partial<Interceptor<AxiosResponse>>
|
||||
)[] = [];
|
||||
|
||||
this.interceptors.request.forEach((requestInterceptor) => {
|
||||
this.#eacheRequestInterceptors((requestInterceptor) => {
|
||||
chain.unshift(requestInterceptor);
|
||||
});
|
||||
chain.push(requestHandler);
|
||||
this.interceptors.response.forEach((responseInterceptor) => {
|
||||
this.#eacheResponseInterceptors((responseInterceptor) => {
|
||||
chain.push(responseInterceptor);
|
||||
});
|
||||
chain.push(errorHandler);
|
||||
|
@ -374,4 +500,49 @@ export default class Axios extends AxiosDomain {
|
|||
Promise.resolve(config),
|
||||
) as Promise<AxiosResponse>;
|
||||
}
|
||||
|
||||
#eacheRequestInterceptors(executor: InterceptorExecutor<AxiosRequestConfig>) {
|
||||
this.interceptors.request.forEach(executor);
|
||||
if (this.#parent) {
|
||||
this.#parent.#eacheRequestInterceptors(executor);
|
||||
}
|
||||
}
|
||||
|
||||
#eacheResponseInterceptors(executor: InterceptorExecutor<AxiosResponse>) {
|
||||
this.interceptors.response.forEach(executor);
|
||||
if (this.#parent) {
|
||||
this.#parent.#eacheResponseInterceptors(executor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const method of PLAIN_METHODS) {
|
||||
Axios.prototype[method] = function processRequestMethod(url, config = {}) {
|
||||
config.method = method;
|
||||
return this.request(url, config);
|
||||
};
|
||||
}
|
||||
|
||||
for (const method of WITH_PARAMS_METHODS) {
|
||||
Axios.prototype[method] = function processRequestMethodWithParams(
|
||||
url,
|
||||
params = {},
|
||||
config = {},
|
||||
) {
|
||||
config.method = method;
|
||||
config.params = deepMerge(params, config.params ?? {});
|
||||
return this.request(url, config);
|
||||
};
|
||||
}
|
||||
|
||||
for (const method of WITH_DATA_METHODS) {
|
||||
Axios.prototype[method] = function processRequestMethodWithData(
|
||||
url,
|
||||
data,
|
||||
config = {},
|
||||
) {
|
||||
config.method = method;
|
||||
config.data = data;
|
||||
return this.request(url, config);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,205 +0,0 @@
|
|||
import {
|
||||
PLAIN_METHODS,
|
||||
WITH_DATA_METHODS,
|
||||
WITH_PARAMS_METHODS,
|
||||
} from '../constants/methods';
|
||||
import { isString, isUndefined } from '../helpers/isTypes';
|
||||
import { deepMerge } from '../helpers/deepMerge';
|
||||
import { combineURL } from '../helpers/combineURL';
|
||||
import { dispatchRequest } from '../request/dispatchRequest';
|
||||
import { mergeConfig } from './mergeConfig';
|
||||
import {
|
||||
AxiosRequestConfig,
|
||||
AxiosRequestData,
|
||||
AxiosResponse,
|
||||
AxiosResponseData,
|
||||
} from './Axios';
|
||||
import MiddlewareManager, {
|
||||
MiddlewareContext,
|
||||
MiddlewareFlush,
|
||||
} from './MiddlewareManager';
|
||||
|
||||
/**
|
||||
* 请求函数
|
||||
*/
|
||||
export interface AxiosDomainRequest {
|
||||
<TData extends AxiosResponseData>(config: AxiosRequestConfig): Promise<
|
||||
AxiosResponse<TData>
|
||||
>;
|
||||
<TData extends AxiosResponseData>(
|
||||
url: string,
|
||||
config?: AxiosRequestConfig,
|
||||
): Promise<AxiosResponse<TData>>;
|
||||
}
|
||||
|
||||
/**
|
||||
* 普通的请求方法
|
||||
*/
|
||||
export type AxiosDomainRequestMethod = <TData extends AxiosResponseData>(
|
||||
url: string,
|
||||
config?: AxiosRequestConfig,
|
||||
) => Promise<AxiosResponse<TData>>;
|
||||
|
||||
/**
|
||||
* 带参数的请求方法
|
||||
*/
|
||||
export type AxiosDomainRequestMethodWithParams = <
|
||||
TData extends AxiosResponseData,
|
||||
>(
|
||||
url: string,
|
||||
params?: AnyObject,
|
||||
config?: AxiosRequestConfig,
|
||||
) => Promise<AxiosResponse<TData>>;
|
||||
|
||||
/**
|
||||
* 带数据的请求方法
|
||||
*/
|
||||
export type AxiosDomainRequestMethodWithData = <
|
||||
TData extends AxiosResponseData,
|
||||
>(
|
||||
url: string,
|
||||
data?: AxiosRequestData,
|
||||
config?: AxiosRequestConfig,
|
||||
) => Promise<AxiosResponse<TData>>;
|
||||
|
||||
export interface AxiosDomainRequestHandler {
|
||||
(config: AxiosRequestConfig): Promise<AxiosResponse>;
|
||||
}
|
||||
|
||||
export default class AxiosDomain {
|
||||
/**
|
||||
* 默认请求配置
|
||||
*/
|
||||
defaults: AxiosRequestConfig;
|
||||
|
||||
middleware = new MiddlewareManager();
|
||||
|
||||
/**
|
||||
* 发送请求
|
||||
*/
|
||||
request!: AxiosDomainRequest;
|
||||
|
||||
/**
|
||||
* 发送 options 请求
|
||||
*/
|
||||
options!: AxiosDomainRequestMethod;
|
||||
|
||||
/**
|
||||
* 发送 get 请求
|
||||
*/
|
||||
get!: AxiosDomainRequestMethodWithParams;
|
||||
|
||||
/**
|
||||
* 发送 head 请求
|
||||
*/
|
||||
head!: AxiosDomainRequestMethodWithParams;
|
||||
|
||||
/**
|
||||
* 发送 post 请求
|
||||
*/
|
||||
post!: AxiosDomainRequestMethodWithData;
|
||||
|
||||
/**
|
||||
* 发送 put 请求
|
||||
*/
|
||||
put!: AxiosDomainRequestMethodWithData;
|
||||
|
||||
/**
|
||||
* 发送 patch 请求
|
||||
*/
|
||||
patch!: AxiosDomainRequestMethodWithData;
|
||||
|
||||
/**
|
||||
* 发送 delete 请求
|
||||
*/
|
||||
delete!: AxiosDomainRequestMethodWithParams;
|
||||
|
||||
/**
|
||||
* 发送 trace 请求
|
||||
*/
|
||||
trace!: AxiosDomainRequestMethod;
|
||||
|
||||
/**
|
||||
* 发送 connect 请求
|
||||
*/
|
||||
connect!: AxiosDomainRequestMethod;
|
||||
|
||||
flush: MiddlewareFlush;
|
||||
|
||||
constructor(
|
||||
defaults: AxiosRequestConfig,
|
||||
processRequest: (
|
||||
config: AxiosRequestConfig,
|
||||
requestHandler: AxiosDomainRequestHandler,
|
||||
) => Promise<AxiosResponse>,
|
||||
) {
|
||||
this.defaults = defaults;
|
||||
|
||||
this.request = (
|
||||
urlOrConfig: string | AxiosRequestConfig,
|
||||
config: AxiosRequestConfig = {},
|
||||
) => {
|
||||
if (isString(urlOrConfig)) {
|
||||
config.url = urlOrConfig;
|
||||
} else {
|
||||
config = urlOrConfig;
|
||||
}
|
||||
|
||||
if (isUndefined(config.method)) {
|
||||
config.method = 'get';
|
||||
}
|
||||
|
||||
return processRequest(
|
||||
mergeConfig(this.defaults, config),
|
||||
this.#requestHandler,
|
||||
);
|
||||
};
|
||||
this.flush = this.middleware.wrap(async (ctx) => {
|
||||
ctx.res = await dispatchRequest(ctx.req);
|
||||
});
|
||||
}
|
||||
|
||||
#requestHandler: AxiosDomainRequestHandler = async (config) => {
|
||||
config.url = combineURL(config.baseURL, config.url);
|
||||
const ctx: MiddlewareContext = {
|
||||
req: config,
|
||||
res: null,
|
||||
};
|
||||
await this.flush(ctx);
|
||||
return ctx.res as AxiosResponse;
|
||||
};
|
||||
}
|
||||
|
||||
for (const method of PLAIN_METHODS) {
|
||||
AxiosDomain.prototype[method] = function processRequestMethod(
|
||||
url,
|
||||
config = {},
|
||||
) {
|
||||
config.method = method;
|
||||
return this.request(url, config);
|
||||
};
|
||||
}
|
||||
|
||||
for (const method of WITH_PARAMS_METHODS) {
|
||||
AxiosDomain.prototype[method] = function processRequestMethodWithParams(
|
||||
url,
|
||||
params = {},
|
||||
config = {},
|
||||
) {
|
||||
config.method = method;
|
||||
config.params = deepMerge(params, config.params ?? {});
|
||||
return this.request(url, config);
|
||||
};
|
||||
}
|
||||
|
||||
for (const method of WITH_DATA_METHODS) {
|
||||
AxiosDomain.prototype[method] = function processRequestMethodWithData(
|
||||
url,
|
||||
data,
|
||||
config = {},
|
||||
) {
|
||||
config.method = method;
|
||||
config.data = data;
|
||||
return this.request(url, config);
|
||||
};
|
||||
}
|
|
@ -1,36 +1,44 @@
|
|||
import { assert } from '../helpers/error';
|
||||
import { combineURL } from '../helpers/combineURL';
|
||||
import { isFunction } from '../helpers/isTypes';
|
||||
import { AxiosRequestConfig, AxiosResponse } from './Axios';
|
||||
|
||||
export interface MiddlewareContext {
|
||||
req: AxiosRequestConfig;
|
||||
res: null | AxiosResponse;
|
||||
}
|
||||
import { isFunction, isString } from '../helpers/isTypes';
|
||||
|
||||
export interface MiddlewareNext {
|
||||
(): Promise<void>;
|
||||
}
|
||||
|
||||
export interface MiddlewareCallback {
|
||||
(ctx: MiddlewareContext, next: MiddlewareNext): Promise<void>;
|
||||
export interface MiddlewareCallback<Conext extends AnyObject> {
|
||||
(ctx: Conext, next: MiddlewareNext): Promise<void>;
|
||||
}
|
||||
|
||||
export interface MiddlewareFlush {
|
||||
(ctx: MiddlewareContext): Promise<void>;
|
||||
export interface MiddlewareFlush<Conext extends AnyObject> {
|
||||
(ctx: Conext): Promise<void>;
|
||||
}
|
||||
|
||||
export default class MiddlewareManager {
|
||||
#map = new Map<string, MiddlewareCallback[]>();
|
||||
export default class MiddlewareManager<Conext extends AnyObject = AnyObject> {
|
||||
#map = new Map<string, MiddlewareCallback<Conext>[]>();
|
||||
|
||||
use(callback: MiddlewareCallback): MiddlewareManager;
|
||||
use(path: string, callback: MiddlewareCallback): MiddlewareManager;
|
||||
use(path: string | MiddlewareCallback, callback?: MiddlewareCallback) {
|
||||
flush: MiddlewareFlush<Conext>;
|
||||
|
||||
constructor(flush: MiddlewareFlush<Conext>) {
|
||||
this.flush = this.wrap(flush);
|
||||
}
|
||||
|
||||
use(callback: MiddlewareCallback<Conext>): MiddlewareManager<Conext>;
|
||||
use(
|
||||
path: string,
|
||||
callback: MiddlewareCallback<Conext>,
|
||||
): MiddlewareManager<Conext>;
|
||||
use(
|
||||
path: string | MiddlewareCallback<Conext>,
|
||||
callback?: MiddlewareCallback<Conext>,
|
||||
) {
|
||||
if (isFunction(path)) {
|
||||
callback = path;
|
||||
path = '/';
|
||||
}
|
||||
assert(!!path, 'path 不是一个非空的 string');
|
||||
assert(isString(path), 'path 不是一个 string');
|
||||
assert(!!path, 'path 不是一个长度大于零的 string');
|
||||
assert(isFunction(callback), 'callback 不是一个 function');
|
||||
|
||||
const middlewares = this.#map.get(path) ?? [];
|
||||
middlewares.push(callback!);
|
||||
|
@ -39,32 +47,25 @@ export default class MiddlewareManager {
|
|||
return this;
|
||||
}
|
||||
|
||||
wrap(flush: MiddlewareFlush): MiddlewareFlush {
|
||||
return (ctx) => this.#performer(ctx, flush);
|
||||
}
|
||||
|
||||
#performer(ctx: MiddlewareContext, flush: MiddlewareFlush) {
|
||||
const middlewares = [...this.#getAllMiddlewares(ctx), flush];
|
||||
|
||||
function next(): Promise<void> {
|
||||
return middlewares.shift()!(ctx, next);
|
||||
}
|
||||
|
||||
return next();
|
||||
}
|
||||
|
||||
#getAllMiddlewares(ctx: MiddlewareContext) {
|
||||
const allMiddlewares: MiddlewareCallback[] = [];
|
||||
wrap(flush: MiddlewareFlush<Conext>): MiddlewareFlush<Conext> {
|
||||
return (ctx) => {
|
||||
const allMiddlewares: MiddlewareCallback<Conext>[] = [];
|
||||
|
||||
for (const [path, middlewares] of this.#map.entries()) {
|
||||
const url = combineURL(ctx.req.baseURL, path);
|
||||
|
||||
const checkRE = new RegExp(`^${url}([/?].*)?`);
|
||||
if (checkRE.test(ctx.req.url!)) {
|
||||
|
||||
if (path === '/') {
|
||||
allMiddlewares.push(...middlewares);
|
||||
} else if (checkRE.test(ctx.req.url!)) {
|
||||
allMiddlewares.push(...middlewares);
|
||||
}
|
||||
}
|
||||
|
||||
return allMiddlewares;
|
||||
const tasks = [...allMiddlewares, flush];
|
||||
return (function next(): Promise<void> {
|
||||
return tasks.shift()!(ctx, next);
|
||||
})();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
import { combineURL } from '../helpers/combineURL';
|
||||
import { buildURL } from '../helpers/buildURL';
|
||||
import Axios, {
|
||||
AxiosRequest,
|
||||
AxiosRequestConfig,
|
||||
AxiosRequestHeaders,
|
||||
} from './Axios';
|
||||
import { mergeConfig } from './mergeConfig';
|
||||
|
||||
/**
|
||||
* axios 实例默认配置
|
||||
*/
|
||||
export interface AxiosInstanceDefaults extends AxiosRequestConfig {
|
||||
/**
|
||||
* 请求头
|
||||
*/
|
||||
headers: Required<AxiosRequestHeaders>;
|
||||
}
|
||||
|
||||
/**
|
||||
* axios 实例
|
||||
*/
|
||||
export interface AxiosInstance extends AxiosRequest, Axios {
|
||||
/**
|
||||
* 默认请求配置
|
||||
*/
|
||||
defaults: AxiosInstanceDefaults;
|
||||
/**
|
||||
* 获取 URI
|
||||
*
|
||||
* @param config 默认配置
|
||||
*/
|
||||
getUri(config: AxiosRequestConfig): string;
|
||||
/**
|
||||
* 派生领域
|
||||
*
|
||||
* @param config 默认配置
|
||||
*/
|
||||
fork(config: AxiosRequestConfig): AxiosInstance;
|
||||
/**
|
||||
* 扩展实例
|
||||
*
|
||||
* @param config 默认配置
|
||||
*/
|
||||
extend(config: AxiosRequestConfig): AxiosInstance;
|
||||
}
|
||||
|
||||
export function createInstance(config: AxiosRequestConfig, parent?: Axios) {
|
||||
const context = new Axios(config, parent);
|
||||
const instance = context.request as AxiosInstance;
|
||||
|
||||
instance.getUri = function getUri(config: AxiosRequestConfig) {
|
||||
const { url, params, paramsSerializer } = mergeConfig(
|
||||
instance.defaults,
|
||||
config,
|
||||
);
|
||||
return buildURL(url, params, paramsSerializer).replace(/^\?/, '');
|
||||
};
|
||||
instance.extend = function extend(config: AxiosRequestConfig = {}) {
|
||||
config.url = combineURL(instance.defaults.baseURL, config.url);
|
||||
return createInstance(mergeConfig(instance.defaults, config), context);
|
||||
};
|
||||
instance.fork = instance.extend;
|
||||
|
||||
Object.assign(instance, context);
|
||||
Object.setPrototypeOf(instance, Axios.prototype);
|
||||
|
||||
return instance;
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
import { getDefaultAdapter } from './adpater/getDefaultAdapter';
|
||||
import { AxiosInstanceDefaults } from './axios';
|
||||
import { AxiosInstanceDefaults } from './core/createInstance';
|
||||
|
||||
const defaults: AxiosInstanceDefaults = {
|
||||
// 适配器,在支持的平台中有值。
|
||||
|
|
11
src/index.ts
11
src/index.ts
|
@ -15,10 +15,13 @@ export type {
|
|||
AxiosUploadProgressCallback,
|
||||
} from './core/Axios';
|
||||
export type {
|
||||
MiddlewareContext,
|
||||
MiddlewareCallback,
|
||||
MiddlewareNext,
|
||||
} from './core/MiddlewareManager';
|
||||
export type {
|
||||
AxiosInstanceDefaults,
|
||||
AxiosInstance,
|
||||
} from './core/createInstance';
|
||||
export type {
|
||||
AxiosAdapter,
|
||||
AxiosAdapterRequestConfig,
|
||||
|
@ -35,11 +38,7 @@ export type {
|
|||
AxiosAdapterPlatform,
|
||||
AxiosAdapterPlatformTask,
|
||||
} from './adpater/createAdapter';
|
||||
export type {
|
||||
AxiosInstanceDefaults,
|
||||
AxiosInstance,
|
||||
AxiosStatic,
|
||||
} from './axios';
|
||||
export type { AxiosStatic } from './axios';
|
||||
|
||||
export { CancelToken, isCancel } from './request/cancel';
|
||||
export { isAxiosError } from './request/createError';
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
import { describe, test, expect, vi, afterEach } from 'vitest';
|
||||
import { describe, test, expect, vi } from 'vitest';
|
||||
import { mockAdapter, testEachMethods } from 'scripts/test.utils';
|
||||
import {
|
||||
PLAIN_METHODS,
|
||||
WITH_DATA_METHODS,
|
||||
WITH_PARAMS_METHODS,
|
||||
} from '@/constants/methods';
|
||||
import AxiosDomain from '@/core/AxiosDomain';
|
||||
import defaults from '@/defaults';
|
||||
import axios from '@/axios';
|
||||
|
||||
|
@ -163,8 +162,4 @@ describe('src/axios.ts', () => {
|
|||
'test?id=1',
|
||||
);
|
||||
});
|
||||
|
||||
test('派生的领域应该为 AxiosDomain 的实例', () => {
|
||||
expect(axios.fork() instanceof AxiosDomain).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -10,7 +10,6 @@ import {
|
|||
WITH_DATA_METHODS,
|
||||
WITH_PARAMS_METHODS,
|
||||
} from '@/constants/methods';
|
||||
import AxiosDomain from '@/core/AxiosDomain';
|
||||
import Axios from '@/core/Axios';
|
||||
import axios from '@/axios';
|
||||
|
||||
|
@ -22,10 +21,6 @@ describe('src/core/Axios.ts', () => {
|
|||
baseURL: 'http://api.com',
|
||||
});
|
||||
|
||||
test('应该继承自 AxiosDomain', () => {
|
||||
expect(new Axios() instanceof AxiosDomain).toBeTruthy();
|
||||
});
|
||||
|
||||
test('应该有这些实例属性及方法', () => {
|
||||
const c = {
|
||||
baseURL: 'http://api.com',
|
||||
|
@ -34,8 +29,6 @@ describe('src/core/Axios.ts', () => {
|
|||
expect(axiosObj.defaults).toEqual(c);
|
||||
expect(axiosObj.interceptors).toBeTypeOf('object');
|
||||
expect(axiosObj.request).toBeTypeOf('function');
|
||||
expect(axiosObj.getUri).toBeTypeOf('function');
|
||||
expect(axiosObj.fork).toBeTypeOf('function');
|
||||
});
|
||||
|
||||
testEachMethods('%s 应该是一个函数', (k) => {
|
||||
|
@ -354,45 +347,4 @@ describe('src/core/Axios.ts', () => {
|
|||
expect(res2).not.toBeCalled();
|
||||
expect(rej2).toBeCalled();
|
||||
});
|
||||
|
||||
test('应该可以获取 URI', () => {
|
||||
expect(axiosObj.getUri({ url: 'test' })).toBe('test');
|
||||
expect(axiosObj.getUri({ url: 'test', params: { id: 1 } })).toBe(
|
||||
'test?id=1',
|
||||
);
|
||||
expect(
|
||||
axiosObj.getUri({ url: 'test', paramsSerializer: () => 'id=1' }),
|
||||
).toBe('test?id=1');
|
||||
});
|
||||
|
||||
test('派生的领域应该为 AxiosDomain 的实例', () => {
|
||||
expect(axiosObj.fork() instanceof AxiosDomain).toBeTruthy();
|
||||
});
|
||||
|
||||
test('应该支持 绝对路径/相对路径 派生领域', () => {
|
||||
const a1 = axiosObj.fork({ baseURL: 'test' });
|
||||
const a2 = new Axios().fork({ baseURL: 'test' });
|
||||
const a3 = axiosObj.fork({ baseURL: 'https://api.com' });
|
||||
const a4 = axiosObj.fork();
|
||||
|
||||
expect(a1.defaults.baseURL).toBe('http://api.com/test');
|
||||
expect(a2.defaults.baseURL).toBe('/test');
|
||||
expect(a3.defaults.baseURL).toBe('https://api.com');
|
||||
expect(a4.defaults.baseURL).toBe('http://api.com');
|
||||
});
|
||||
|
||||
test('派生自当前实例的领域应该可以复用当前实例的拦截器', async () => {
|
||||
const axiosObj = new Axios();
|
||||
const req = vi.fn((v) => v);
|
||||
const res = vi.fn((v) => v);
|
||||
|
||||
axiosObj.interceptors.request.use(req);
|
||||
axiosObj.interceptors.response.use(res);
|
||||
|
||||
const a = axiosObj.fork({ baseURL: 'test' });
|
||||
await a.request('test', { adapter: mockAdapter() });
|
||||
|
||||
expect(req).toBeCalled();
|
||||
expect(res).toBeCalled();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,240 +0,0 @@
|
|||
import { describe, test, expect, vi } from 'vitest';
|
||||
import { ignore } from '@/helpers/ignore';
|
||||
import {
|
||||
PLAIN_METHODS,
|
||||
WITH_DATA_METHODS,
|
||||
WITH_PARAMS_METHODS,
|
||||
} from '@/constants/methods';
|
||||
import AxiosDomain from '@/core/AxiosDomain';
|
||||
import { AxiosResponse } from '@/core/Axios';
|
||||
import { eachMethods } from 'scripts/test.utils';
|
||||
|
||||
describe('src/core/AxiosDomain.ts', () => {
|
||||
test('应该有这些实例属性', () => {
|
||||
const c = {
|
||||
baseURL: 'http://api.com',
|
||||
};
|
||||
const a = new AxiosDomain(c, vi.fn());
|
||||
|
||||
expect(a.defaults).toEqual(c);
|
||||
expect(a.request).toBeTypeOf('function');
|
||||
|
||||
eachMethods((k) => {
|
||||
expect(a[k]).toBeTypeOf('function');
|
||||
});
|
||||
});
|
||||
|
||||
test('发送请求时 processRequest 应该被调用', () => {
|
||||
const p = vi.fn();
|
||||
const d = {
|
||||
baseURL: 'http://api.com',
|
||||
};
|
||||
const c = {
|
||||
url: 'test',
|
||||
params: {
|
||||
id: 1,
|
||||
},
|
||||
data: {
|
||||
id: 1,
|
||||
},
|
||||
};
|
||||
|
||||
new AxiosDomain(d, p).request(c);
|
||||
|
||||
expect(p).toBeCalled();
|
||||
expect(p.mock.calls[0][0]).toEqual({
|
||||
...d,
|
||||
...c,
|
||||
});
|
||||
});
|
||||
|
||||
test('请求方法应该支持空参数', () => {
|
||||
const cb = vi.fn();
|
||||
const a = new AxiosDomain({}, cb);
|
||||
|
||||
a.request('test');
|
||||
|
||||
PLAIN_METHODS.forEach((k) => a[k]('test'));
|
||||
WITH_PARAMS_METHODS.forEach((k) => a[k]('test'));
|
||||
WITH_DATA_METHODS.forEach((k) => a[k]('test'));
|
||||
|
||||
const l =
|
||||
PLAIN_METHODS.length +
|
||||
WITH_PARAMS_METHODS.length +
|
||||
WITH_DATA_METHODS.length +
|
||||
1;
|
||||
expect(cb.mock.calls.length).toBe(l);
|
||||
cb.mock.calls.forEach(([config]) => expect(config.url).toBe('test'));
|
||||
});
|
||||
|
||||
test('应该可以调用请求方法', () => {
|
||||
const cb = vi.fn();
|
||||
const d = {
|
||||
baseURL: 'http://api.com',
|
||||
};
|
||||
const u = 'test';
|
||||
const c = {
|
||||
params: {
|
||||
id: 1,
|
||||
},
|
||||
data: {
|
||||
id: 1,
|
||||
},
|
||||
};
|
||||
const a = new AxiosDomain(d, async (config) => {
|
||||
cb();
|
||||
|
||||
expect(config.baseURL).toBe(d.baseURL);
|
||||
expect(config.url).toBe(u);
|
||||
expect(config.params).toEqual(c.params);
|
||||
expect(config.data).toEqual(c.data);
|
||||
|
||||
return {} as AxiosResponse;
|
||||
});
|
||||
|
||||
a.request(u, c);
|
||||
|
||||
PLAIN_METHODS.forEach((k) => a[k](u, c));
|
||||
WITH_PARAMS_METHODS.forEach((k) => a[k](u, c.params, ignore(c, 'params')));
|
||||
WITH_DATA_METHODS.forEach((k) => a[k](u, c.data, ignore(c, 'data')));
|
||||
|
||||
const l =
|
||||
PLAIN_METHODS.length +
|
||||
WITH_PARAMS_METHODS.length +
|
||||
WITH_DATA_METHODS.length +
|
||||
1;
|
||||
expect(cb.mock.calls.length).toBe(l);
|
||||
});
|
||||
|
||||
test('应该可以直接传递 config 调用请求方法', () => {
|
||||
const cb = vi.fn();
|
||||
const d = {
|
||||
baseURL: 'http://api.com',
|
||||
};
|
||||
const c = {
|
||||
url: 'test',
|
||||
params: {
|
||||
id: 1,
|
||||
},
|
||||
data: {
|
||||
id: 1,
|
||||
},
|
||||
};
|
||||
const a = new AxiosDomain(d, async (config) => {
|
||||
cb();
|
||||
|
||||
expect(config.baseURL).toBe(d.baseURL);
|
||||
expect(config.url).toBe(c.url);
|
||||
expect(config.params).toEqual(c.params);
|
||||
expect(config.data).toEqual(c.data);
|
||||
|
||||
return {} as AxiosResponse;
|
||||
});
|
||||
|
||||
a.request(c);
|
||||
|
||||
PLAIN_METHODS.forEach((k) => a[k](c.url, ignore(c, 'url')));
|
||||
WITH_PARAMS_METHODS.forEach((k) =>
|
||||
a[k](c.url, c.params, ignore(c, 'url', 'params')),
|
||||
);
|
||||
WITH_DATA_METHODS.forEach((k) =>
|
||||
a[k](c.url, c.data, ignore(c, 'url', 'data')),
|
||||
);
|
||||
|
||||
const l =
|
||||
PLAIN_METHODS.length +
|
||||
WITH_PARAMS_METHODS.length +
|
||||
WITH_DATA_METHODS.length +
|
||||
1;
|
||||
expect(cb.mock.calls.length).toBe(l);
|
||||
});
|
||||
|
||||
test('应该支持深度合并 params', () => {
|
||||
const d = {
|
||||
baseURL: 'http://api.com',
|
||||
};
|
||||
const p = {
|
||||
v1: 1,
|
||||
v2: {
|
||||
v1: 1,
|
||||
},
|
||||
};
|
||||
const c = {
|
||||
params: {
|
||||
v2: {
|
||||
v2: 2,
|
||||
},
|
||||
v3: 3,
|
||||
},
|
||||
};
|
||||
|
||||
const a = new AxiosDomain(d, async (config) => {
|
||||
expect(config.params).toEqual({
|
||||
v1: 1,
|
||||
v2: {
|
||||
v1: 1,
|
||||
v2: 2,
|
||||
},
|
||||
v3: 3,
|
||||
});
|
||||
|
||||
return {} as AxiosResponse;
|
||||
});
|
||||
|
||||
WITH_PARAMS_METHODS.forEach((k) => a[k]('test', p, c));
|
||||
});
|
||||
|
||||
test('应该只取传入的 data', () => {
|
||||
const ds = {
|
||||
baseURL: 'http://api.com',
|
||||
};
|
||||
const d = {
|
||||
v: 1,
|
||||
};
|
||||
const c = {
|
||||
v: 2,
|
||||
};
|
||||
|
||||
const a = new AxiosDomain(ds, async (config) => {
|
||||
expect(config.data).toEqual({
|
||||
v: 1,
|
||||
});
|
||||
|
||||
return {} as AxiosResponse;
|
||||
});
|
||||
|
||||
WITH_DATA_METHODS.forEach((k) => a[k]('test', d, c));
|
||||
});
|
||||
|
||||
test('应该支持多种类型 data', () => {
|
||||
const ds = {
|
||||
baseURL: 'http://api.com',
|
||||
};
|
||||
|
||||
const str = '11';
|
||||
const obj = {};
|
||||
const buff = new ArrayBuffer(0);
|
||||
|
||||
const testStr = new AxiosDomain(ds, async (config) => {
|
||||
expect(config.data).toBe(str);
|
||||
|
||||
return {} as AxiosResponse;
|
||||
});
|
||||
const testObj = new AxiosDomain(ds, async (config) => {
|
||||
expect(config.data).toBe(obj);
|
||||
|
||||
return {} as AxiosResponse;
|
||||
});
|
||||
const testBuff = new AxiosDomain(ds, async (config) => {
|
||||
expect(config.data).toBe(buff);
|
||||
|
||||
return {} as AxiosResponse;
|
||||
});
|
||||
|
||||
WITH_DATA_METHODS.forEach((k) => {
|
||||
testStr[k]('test', str);
|
||||
testObj[k]('test', obj);
|
||||
testBuff[k]('test', buff);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -3,14 +3,18 @@ import MiddlewareManager from '@/core/MiddlewareManager';
|
|||
|
||||
describe('src/core/MiddlewareManager.ts', () => {
|
||||
test('应该有这些实例属性', () => {
|
||||
const m = new MiddlewareManager();
|
||||
const m = new MiddlewareManager(vi.fn());
|
||||
|
||||
expect(m.use).toBeTypeOf('function');
|
||||
expect(m.wrap).toBeTypeOf('function');
|
||||
});
|
||||
|
||||
test('应该可以添加中间件回调', async () => {
|
||||
const m = new MiddlewareManager();
|
||||
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,
|
||||
|
@ -24,20 +28,20 @@ describe('src/core/MiddlewareManager.ts', () => {
|
|||
await next();
|
||||
expect(ctx.res).toBe(res);
|
||||
});
|
||||
const flush = vi.fn(async (ctx) => {
|
||||
expect(ctx.req.url).toBe('test');
|
||||
ctx.res = res;
|
||||
});
|
||||
|
||||
m.use(midde);
|
||||
await m.wrap(flush)(ctx);
|
||||
await m.flush(ctx);
|
||||
|
||||
expect(ctx.res).toBe(res);
|
||||
expect(midde).toBeCalled();
|
||||
});
|
||||
|
||||
test('应该可以给路径添加中间件回调', async () => {
|
||||
const m = new MiddlewareManager();
|
||||
const flush = vi.fn(async (ctx) => {
|
||||
ctx.res = res;
|
||||
});
|
||||
|
||||
const m = new MiddlewareManager(flush);
|
||||
const ctx1 = {
|
||||
req: {
|
||||
baseURL: 'https://api.com',
|
||||
|
@ -60,18 +64,15 @@ describe('src/core/MiddlewareManager.ts', () => {
|
|||
await next();
|
||||
expect(ctx.res).toBe(res);
|
||||
});
|
||||
const flush = vi.fn(async (ctx) => {
|
||||
ctx.res = res;
|
||||
});
|
||||
|
||||
m.use('/test', midde);
|
||||
await m.wrap(flush)(ctx1);
|
||||
await m.flush(ctx1);
|
||||
|
||||
expect(ctx1.res).toBe(res);
|
||||
expect(midde).not.toBeCalled();
|
||||
|
||||
m.use('/test', midde);
|
||||
await m.wrap(flush)(ctx2);
|
||||
await m.flush(ctx2);
|
||||
|
||||
expect(midde).toBeCalled();
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue