axios-miniprogram/src/core/Axios.ts

598 lines
11 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import {
PLAIN_METHODS,
WITH_DATA_METHODS,
WITH_PARAMS_METHODS,
} from '../constants/methods';
import { isString } from '../helpers/types';
import { dispatchRequest } from '../request/dispatchRequest';
import { CancelToken } from '../request/cancel';
import { AxiosTransformer } from '../request/transformData';
import { deepMerge } from '../helpers/deepMerge';
import {
AxiosAdapter,
AxiosAdapterRequestMethod,
AxiosAdapterPlatformTask,
AxiosAdapterRequestConfig,
AxiosAdapterResponseData,
} from '../adpater/createAdapter';
import InterceptorManager, {
Interceptor,
InterceptorExecutor,
} from './InterceptorManager';
import MiddlewareManager, {
MiddlewareCallback,
MiddlewareContext,
} from './MiddlewareManager';
import { mergeConfig } from './mergeConfig';
/**
* 请求方法
*/
export type AxiosRequestMethod =
| AxiosAdapterRequestMethod
| 'options'
| 'get'
| 'head'
| 'post'
| 'put'
| 'patch'
| 'delete'
| 'trace'
| 'connect';
/**
* 请求头
*/
export interface AxiosRequestHeaders extends AnyObject {
/**
* 通用请求头
*/
common?: AnyObject;
/**
* options 请求头
*/
options?: AnyObject;
/**
* get 请求头
*/
get?: AnyObject;
/**
* head 请求头
*/
head?: AnyObject;
/**
* post 请求头
*/
post?: AnyObject;
/**
* put 请求头
*/
put?: AnyObject;
/**
* delete 请求头
*/
delete?: AnyObject;
/**
* trace 请求头
*/
trace?: AnyObject;
/**
* connect 请求头
*/
connect?: AnyObject;
}
/**
* 表单数据(上传会用到)
*/
export interface AxiosRequestFormData extends AnyObject {
/**
* 文件名
*/
name: string;
/**
* 文件路径
*/
filePath: string;
}
/**
* 请求数据
*/
export type AxiosRequestData =
| string
| AnyObject
| ArrayBuffer
| AxiosRequestFormData;
/**
* 响应数据
*/
export type AxiosResponseData = number | AxiosAdapterResponseData;
/**
* 进度对象
*/
export interface AxiosProgressEvent extends AnyObject {
/**
* 上传进度百分比
*/
progress: number;
}
/**
* 下载进度对象
*/
export interface AxiosDownloadProgressEvent extends AxiosProgressEvent {
/**
* 已经下载的数据长度,单位 Bytes
*/
totalBytesWritten: number;
/**
* 预预期需要下载的数据总长度,单位 Bytes
*/
totalBytesExpectedToWrite: number;
}
/**
* 监听下载进度
*/
export interface AxiosDownloadProgressCallback {
(event: AxiosDownloadProgressEvent): void;
}
/**
* 上传进度对象
*/
export interface AxiosUploadProgressEvent extends AxiosProgressEvent {
/**
* 已经上传的数据长度,单位 Bytes
*/
totalBytesSent: number;
/**
* 预期需要上传的数据总长度,单位 Bytes
*/
totalBytesExpectedToSend: number;
}
/**
* 监听上传进度
*/
export interface AxiosUploadProgressCallback {
(event: AxiosUploadProgressEvent): void;
}
/**
* 请求配置
*/
export interface AxiosRequestConfig
extends Partial<
Omit<AxiosAdapterRequestConfig, 'type' | 'success' | 'fail'>
> {
/**
* 请求适配器
*/
adapter?: AxiosAdapter;
/**
* 基础路径
*/
baseURL?: string;
/**
* 请求的 URL
*/
url?: string;
/**
* 请求参数
*/
params?: AnyObject;
/**
* 请求数据
*/
data?: AxiosRequestData;
/**
* 请求头
*/
headers?: AxiosRequestHeaders;
/**
* 请求方法
*/
method?: AxiosRequestMethod;
/**
* 取消令牌
*/
cancelToken?: CancelToken;
/**
* 下载文件
*/
download?: boolean;
/**
* 上传文件
*/
upload?: boolean;
/**
* 请求参数系列化函数
*/
paramsSerializer?: (params?: AnyObject) => string;
/**
* 校验状态码
*/
validateStatus?: (status: number) => boolean;
/**
* 转换请求数据
*/
transformRequest?: AxiosTransformer<AxiosRequestData>;
/**
* 转换响应数据
*/
transformResponse?: AxiosTransformer<AxiosResponseData>;
/**
* 错误处理
*/
errorHandler?: (error: unknown) => Promise<AxiosResponse>;
/**
* 监听下载进度
*/
onDownloadProgress?: AxiosUploadProgressCallback;
/**
* 监听上传进度
*/
onUploadProgress?: AxiosUploadProgressCallback;
}
/**
* 响应体
*/
export interface AxiosResponse<
TData extends AxiosResponseData = AxiosResponseData,
> extends AnyObject {
/**
* 状态码
*/
status: number;
/**
* 状态字符
*/
statusText: string;
/**
* 响应头
*/
headers: AnyObject;
/**
* 响应数据
*/
data: TData;
/**
* 请求配置
*/
config: AxiosRequestConfig;
/**
* 请求任务
*/
request?: AxiosAdapterPlatformTask;
}
/**
* 错误体
*/
export interface AxiosResponseError extends AnyObject {
/**
* 状态码
*/
status: number;
/**
* 状态字符
*/
statusText: string;
/**
* 响应头
*/
headers: AnyObject;
/**
* 错误数据
*/
data: AnyObject;
/**
* 失败的请求,指没能够成功响应的请求
*/
isFail: true;
/**
* 请求配置
*/
config: AxiosRequestConfig;
/**
* 请求任务
*/
request?: AxiosAdapterPlatformTask;
}
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 构造函数
*/
export interface AxiosConstructor {
new (config: AxiosRequestConfig): Axios;
}
export default class Axios {
/**
* @internal
*
* 父级实例
*/
private declare parent?: Axios;
/**
* 默认请求配置
*/
declare defaults: AxiosRequestConfig;
/**
* 拦截器
*/
interceptors = {
/**
* 请求拦截器
*/
request: new InterceptorManager<AxiosRequestConfig>(),
/**
* 响应拦截器
*/
response: new InterceptorManager<AxiosResponse>(),
};
/**
* @internal
*
* 中间件
*/
private middleware = new MiddlewareManager();
/**
* 发送 options 请求
*/
declare options: AxiosRequestMethodFn;
/**
* 发送 get 请求
*/
declare get: AxiosRequestMethodFnWithParams;
/**
* 发送 head 请求
*/
declare head: AxiosRequestMethodFnWithParams;
/**
* 发送 post 请求
*/
declare post: AxiosRequestMethodFnWithData;
/**
* 发送 put 请求
*/
declare put: AxiosRequestMethodFnWithData;
/**
* 发送 patch 请求
*/
declare patch: AxiosRequestMethodFnWithData;
/**
* 发送 delete 请求
*/
declare delete: AxiosRequestMethodFnWithParams;
/**
* 发送 trace 请求
*/
declare trace: AxiosRequestMethodFn;
/**
* 发送 connect 请求
*/
declare connect: AxiosRequestMethodFn;
/**
* @param config 默认配置
* @param parent 父级实例
*/
constructor(config: AxiosRequestConfig, parent?: Axios) {
this.defaults = config;
this.parent = parent;
}
/**
* 发送请求
*/
request: AxiosRequest = (
urlOrConfig: string | AxiosRequestConfig,
config: AxiosRequestConfig = {},
) => {
if (isString(urlOrConfig)) {
config.url = urlOrConfig;
} else {
config = urlOrConfig;
}
config = mergeConfig(this.defaults, config);
config.method = (config.method?.toLowerCase() ??
'get') as AxiosRequestMethod;
const requestHandler = {
resolved: this.handleRequest,
};
const errorHandler = {
rejected: config.errorHandler,
};
const chain: (
| Partial<Interceptor<AxiosRequestConfig>>
| Partial<Interceptor<AxiosResponse>>
)[] = [];
this.eachInterceptors('request', (interceptor) => {
chain.unshift(interceptor);
});
chain.push(requestHandler);
this.eachInterceptors('response', (interceptor) => {
chain.push(interceptor);
});
chain.push(errorHandler);
return chain.reduce(
(next, { resolved, rejected }) =>
next.then(
// @ts-ignore
resolved,
rejected,
),
Promise.resolve(config),
) as Promise<AxiosResponse>;
};
/**
* @internal
*/
private eachInterceptors<T extends 'request' | 'response'>(
type: T,
executor: InterceptorExecutor<
T extends 'request' ? AxiosRequestConfig : AxiosResponse
>,
) {
// @ts-ignore
this.interceptors[type].forEach(executor);
if (this.parent) {
this.parent.eachInterceptors(type, executor);
}
}
/**
* 注册中间件
*
* 示例1注册一个中间件
* ```ts
* axios.use(async function middleware(ctx, next) {
* console.log(ctx.req);
* await next();
* console.log(ctx.res);
* });
* ```
*
* 示例2链式注册多个中间件
* ```ts
* axios
* .use(async function middleware1(ctx, next) {
* console.log(ctx.req);
* await next();
* console.log(ctx.res);
* })
* .use(async function middleware2(ctx, next) {
* console.log(ctx.req);
* await next();
* console.log(ctx.res);
* });
* ```
*/
use = (middleware: MiddlewareCallback) => {
this.middleware.use(middleware);
return this;
};
/**
* @internal
*/
private handleRequest = async (config: AxiosRequestConfig) => {
const ctx = this.middleware.createContext(config);
await this.run(ctx, this.handleResponse);
return ctx.res as AxiosResponse;
};
/**
* @internal
*/
private async handleResponse(ctx: MiddlewareContext) {
ctx.res = await dispatchRequest(ctx.req);
}
/**
* @internal
*/
private run = (
ctx: MiddlewareContext,
respond: MiddlewareCallback,
): Promise<void> => {
if (!this.parent) {
return this.middleware.run(ctx, respond);
}
return this.middleware.enhanceRun(this.parent.run)(ctx, respond);
};
}
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);
};
}