Commit 136cbb1e3bc056c88cfa21fca612d3ab72b4d119
Committed by
GitHub
1 parent
78535bdd
feat: add request retry (#1553)
Showing
11 changed files
with
111 additions
and
3 deletions
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
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
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'; | ... | ... |