Commit 136cbb1e3bc056c88cfa21fca612d3ab72b4d119

Authored by Captain
Committed by GitHub
1 parent 78535bdd

feat: add request retry (#1553)

mock/sys/user.ts
... ... @@ -111,4 +111,12 @@ export default [
111 111 return resultSuccess(undefined, { message: 'Token has been destroyed' });
112 112 },
113 113 },
  114 + {
  115 + url: '/basic-api/testRetry',
  116 + statusCode: 405,
  117 + method: 'get',
  118 + response: () => {
  119 + return resultError('Error!');
  120 + },
  121 + },
114 122 ] as MockMethod[];
... ...
src/api/sys/user.ts
... ... @@ -8,6 +8,7 @@ enum Api {
8 8 Logout = '/logout',
9 9 GetUserInfo = '/getUserInfo',
10 10 GetPermCode = '/getPermCode',
  11 + TestRetry = '/testRetry',
11 12 }
12 13  
13 14 /**
... ... @@ -39,3 +40,16 @@ export function getPermCode() {
39 40 export function doLogout() {
40 41 return defHttp.get({ url: Api.Logout });
41 42 }
  43 +
  44 +export function testRetry() {
  45 + return defHttp.get(
  46 + { url: Api.TestRetry },
  47 + {
  48 + retryRequest: {
  49 + isOpenRetry: true,
  50 + count: 5,
  51 + waitTime: 1000,
  52 + },
  53 + },
  54 + );
  55 +}
... ...
src/locales/lang/en/routes/demo.ts
... ... @@ -92,6 +92,7 @@ export default {
92 92 breadcrumb: 'Breadcrumbs',
93 93 breadcrumbFlat: 'Flat Mode',
94 94 breadcrumbFlatDetail: 'Flat mode details',
  95 + requestDemo: 'Retry request demo',
95 96  
96 97 breadcrumbChildren: 'Level mode',
97 98 breadcrumbChildrenDetail: 'Level mode detail',
... ...
src/locales/lang/zh-CN/routes/demo.ts
... ... @@ -88,6 +88,7 @@ export default {
88 88 ws: 'websocket测试',
89 89 breadcrumb: '面包屑导航',
90 90 breadcrumbFlat: '平级模式',
  91 + requestDemo: '测试请求重试',
91 92 breadcrumbFlatDetail: '平级详情',
92 93 breadcrumbChildren: '层级模式',
93 94 breadcrumbChildrenDetail: '层级详情',
... ...
src/router/routes/modules/demo/feat.ts
... ... @@ -32,6 +32,15 @@ const feat: AppRouteModule = {
32 32 },
33 33 },
34 34 {
  35 + path: 'request',
  36 + name: 'RequestDemo',
  37 + // @ts-ignore
  38 + component: () => import('/@/views/demo/feat/request-demo/index.vue'),
  39 + meta: {
  40 + title: t('routes.demo.feat.requestDemo'),
  41 + },
  42 + },
  43 + {
35 44 path: 'session-timeout',
36 45 name: 'SessionTimeout',
37 46 component: () => import('/@/views/demo/feat/session-timeout/index.vue'),
... ...
src/utils/http/axios/Axios.ts
... ... @@ -111,7 +111,10 @@ export class VAxios {
111 111 // Response result interceptor error capture
112 112 responseInterceptorsCatch &&
113 113 isFunction(responseInterceptorsCatch) &&
114   - this.axiosInstance.interceptors.response.use(undefined, responseInterceptorsCatch);
  114 + this.axiosInstance.interceptors.response.use(undefined, (error) => {
  115 + // @ts-ignore
  116 + responseInterceptorsCatch(this.axiosInstance, error);
  117 + });
115 118 }
116 119  
117 120 /**
... ...
src/utils/http/axios/axiosRetry.ts 0 → 100644
  1 +import { AxiosError, AxiosInstance } from 'axios';
  2 +/**
  3 + * 请求重试机制
  4 + */
  5 +
  6 +export class AxiosRetry {
  7 + /**
  8 + * 重试
  9 + */
  10 + retry(AxiosInstance: AxiosInstance, error: AxiosError) {
  11 + // @ts-ignore
  12 + const { config } = error.response;
  13 + const { waitTime, count } = config?.requestOptions?.retryRequest;
  14 + config.__retryCount = config.__retryCount || 0;
  15 + if (config.__retryCount >= count) {
  16 + return Promise.reject(error);
  17 + }
  18 + config.__retryCount += 1;
  19 + return this.delay(waitTime).then(() => AxiosInstance(config));
  20 + }
  21 +
  22 + /**
  23 + * 延迟
  24 + */
  25 + private delay(waitTime: number) {
  26 + return new Promise((resolve) => setTimeout(resolve, waitTime));
  27 + }
  28 +}
... ...
src/utils/http/axios/axiosTransform.ts
... ... @@ -48,5 +48,5 @@ export abstract class AxiosTransform {
48 48 /**
49 49 * @description: 请求之后的拦截器错误处理
50 50 */
51   - responseInterceptorsCatch?: (error: Error) => void;
  51 + responseInterceptorsCatch?: (axiosInstance: AxiosResponse, error: Error) => void;
52 52 }
... ...
src/utils/http/axios/index.ts
... ... @@ -17,6 +17,7 @@ import { useErrorLogStoreWithOut } from '/@/store/modules/errorLog';
17 17 import { useI18n } from '/@/hooks/web/useI18n';
18 18 import { joinTimestamp, formatRequestDate } from './helper';
19 19 import { useUserStoreWithOut } from '/@/store/modules/user';
  20 +import { AxiosRetry } from '/@/utils/http/axios/axiosRetry';
20 21  
21 22 const globSetting = useGlobSetting();
22 23 const urlPrefix = globSetting.urlPrefix;
... ... @@ -158,7 +159,7 @@ const transform: AxiosTransform = {
158 159 /**
159 160 * @description: 响应错误处理
160 161 */
161   - responseInterceptorsCatch: (error: any) => {
  162 + responseInterceptorsCatch: (axiosInstance: AxiosResponse, error: any) => {
162 163 const { t } = useI18n();
163 164 const errorLogStore = useErrorLogStoreWithOut();
164 165 errorLogStore.addAjaxErrorInfo(error);
... ... @@ -189,6 +190,14 @@ const transform: AxiosTransform = {
189 190 }
190 191  
191 192 checkStatus(error?.response?.status, msg, errorMessageMode);
  193 +
  194 + // 添加自动重试机制 保险起见 只针对GET请求
  195 + const retryRequest = new AxiosRetry();
  196 + const { isOpenRetry } = config.requestOptions.retryRequest;
  197 + config.method?.toUpperCase() === RequestEnum.GET &&
  198 + isOpenRetry &&
  199 + // @ts-ignore
  200 + retryRequest.retry(axiosInstance, error);
192 201 return Promise.reject(error);
193 202 },
194 203 };
... ... @@ -234,6 +243,11 @@ function createAxios(opt?: Partial<CreateAxiosOptions>) {
234 243 ignoreCancelToken: true,
235 244 // 是否携带token
236 245 withToken: true,
  246 + retryRequest: {
  247 + isOpenRetry: true,
  248 + count: 5,
  249 + waitTime: 100,
  250 + },
237 251 },
238 252 },
239 253 opt || {},
... ...
src/views/demo/feat/request-demo/index.vue 0 → 100644
  1 +<template>
  2 + <div class="request-box">
  3 + <a-button @click="handleClick" color="primary"> 点击会重新发起请求5次 </a-button>
  4 + <p>打开浏览器的network面板,可以看到发出了六次请求</p>
  5 + </div>
  6 +</template>
  7 +<script lang="ts" setup>
  8 + import { testRetry } from '/@/api/sys/user';
  9 + // @ts-ignore
  10 + const handleClick = async () => {
  11 + await testRetry();
  12 + };
  13 +</script>
  14 +
  15 +<style lang="less">
  16 + .request-box {
  17 + margin: 50px;
  18 + }
  19 +
  20 + p {
  21 + margin-top: 10px;
  22 + }
  23 +</style>
... ...
types/axios.d.ts
... ... @@ -23,8 +23,15 @@ export interface RequestOptions {
23 23 ignoreCancelToken?: boolean;
24 24 // Whether to send token in header
25 25 withToken?: boolean;
  26 + // 请求重试机制
  27 + retryRequest?: RetryRequest;
26 28 }
27 29  
  30 +export interface RetryRequest {
  31 + isOpenRetry: boolean;
  32 + count: number;
  33 + waitTime: number;
  34 +}
28 35 export interface Result<T = any> {
29 36 code: number;
30 37 type: 'success' | 'error' | 'warning';
... ...