Axios retry 请求重试插件
axios-retry 是一个用于axios的请求重试插件,用于在请求失败时自动重试请求。支持自定义重试规则。
实现源码
ts
import type {
AxiosError,
AxiosResponse,
InternalAxiosRequestConfig
} from 'axios';
export interface IOptions<
T extends AxiosError = AxiosError,
U extends AxiosResponse = AxiosResponse
> {
maxRetryCount: number;
retryDelay: number;
request: (config: InternalAxiosRequestConfig) => Promise<AxiosResponse>;
isRetry: (error?: T, res?: U) => boolean;
beforeRetry?: (retryCount: number, error?: T, res?: U) => void;
failed?: (retryCount: number, error?: T, res?: U) => void;
}
export type IConfig = Partial<IOptions> & Pick<IOptions, 'request'>;
export class AxiosRetryPlugin {
private options: IOptions;
private histories: Map<string, number>;
constructor(options: IConfig) {
this.options = {
request: options.request,
maxRetryCount: options.maxRetryCount ?? 3,
retryDelay: options.retryDelay ?? 1000,
isRetry:
options.isRetry ??
((err) => {
if (err) {
return true;
}
return false;
})
};
this.histories = new Map();
}
static getDataType(obj: any) {
let res = Object.prototype.toString.call(obj).split(' ')[1];
res = res.substring(0, res.length - 1).toLowerCase();
return res;
}
static generateRequestKey(config: InternalAxiosRequestConfig): string {
const { method, url, data, params } = config;
let key = `${method}-${url}`;
try {
switch (AxiosRetryPlugin.getDataType(data)) {
case 'object':
key += `-${JSON.stringify(data)}`;
break;
case 'formdata':
for (const [k, v] of data.entries()) {
if (v instanceof Blob) {
continue;
}
key += `-${k}-${v}`;
}
break;
default:
break;
}
if (AxiosRetryPlugin.getDataType(params) === 'object') {
key += `-${JSON.stringify(params)}`;
}
} catch (e) {
/* empty */
}
return key;
}
private fetch(config: InternalAxiosRequestConfig, beforeRetry?: () => void) {
return new Promise<AxiosResponse>((resolve, reject) => {
if (!this.options.retryDelay || this.options.retryDelay < 0) {
beforeRetry && beforeRetry();
this.options.request(config).then(resolve).catch(reject);
return;
}
setTimeout(() => {
beforeRetry && beforeRetry();
this.options.request(config).then(resolve).catch(reject);
}, this.options.retryDelay);
});
}
private retryRequest(error: AxiosError): Promise<AxiosResponse>;
private retryRequest(error: undefined, res: AxiosResponse): AxiosResponse;
private retryRequest(error?: AxiosError, res?: AxiosResponse) {
if (!this.options.isRetry(error, res)) {
if (error) {
return Promise.reject(error);
}
return res!;
}
let config: InternalAxiosRequestConfig;
if (error) {
if (!error.config) {
return Promise.reject(error);
}
config = error.config;
} else {
config = res!.config;
}
const key = AxiosRetryPlugin.generateRequestKey(config);
const retryCount = this.histories.get(key) || 0;
if (retryCount >= this.options.maxRetryCount) {
this.options.failed?.(retryCount, error, res);
this.histories.delete(key);
if (error) {
return Promise.reject(error);
}
return res!;
}
const newCount = retryCount + 1;
this.histories.set(key, newCount);
return this.fetch(config, () => {
this.options.beforeRetry?.(newCount, error, res);
});
}
responseInterceptorFulfilled(response: AxiosResponse) {
return this.retryRequest(undefined, response);
}
responseInterceptorRejected(error: AxiosError) {
return this.retryRequest(error);
}
}
export default function createAxiosRetryPlugin(config: IConfig) {
const instance = new AxiosRetryPlugin(config);
return {
responseInterceptorFulfilled:
instance.responseInterceptorFulfilled.bind(instance),
responseInterceptorRejected:
instance.responseInterceptorRejected.bind(instance)
};
}基本用法
简单用法
先创建插件实例,然后将插件实例的拦截器方法注册到 axios 拦截器中即可。
ts
import axios from 'axios';
import createAxiosRetryPlugin from '...';
const instance = axios.create();
const retry = createAxiosRetryPlugin({ request: instance.request });
instance.interceptors.response.use(
undefined,
retry.responseInterceptorRejected
);高级用法
可以通过传入配置项来定制插件的行为。
ts
import axios, type { AxiosError, AxiosResponse } from 'axios'
import createAxiosRetryPlugin from '...'
const instance = axios.create()
const retry = createAxiosRetryPlugin({
request: instance.request,
maxRetryCount: 3,
retryDelay: 1000,
isRetry: (error?: AxiosError, res?: AxiosResponse) => {
return error?.response?.status !== 401 && res?.data.statusCode !== 401
}
})
instance.interceptors.response.use(
retry.responseInterceptorFulfilled,
retry.responseInterceptorRejected
)配置项
request
- 类型:
(config: AxiosRequestConfig) => Promise<AxiosResponse> - 默认值:
undefined - 说明:重试请求方法,建议传入
axios实例的request方法 - 必填:是
maxRetryCount
- 类型:
number - 默认值:
3 - 说明:最大重试次数
- 必填:否
retryDelay
- 类型:
number - 默认值:
1000 - 说明:重试延迟时间(毫秒)
- 必填:否
isRetry
- 类型:
(error?: AxiosError, res?: AxiosResponse) => boolean - 默认值:
(err) => { if (err) { return true; } return false; } - 说明:是否重试的判断函数
- 必填:否
beforeRetry
- 类型:
(retryCount: number, error?: AxiosError, res?: AxiosResponse) => void - 默认值:
undefined - 说明:重试前的回调函数,每次重试前都会调用
- 必填:否
failed
- 类型:
(retryCount: number, error?: AxiosError, res?: AxiosResponse) => void - 默认值:
undefined - 说明:重试失败的回调函数,当重试次数达到最大重试次数时调用
- 必填:否