diff --git a/mock/sys/user.ts b/mock/sys/user.ts index 8b8989fa42b..5b569d4d9ac 100644 --- a/mock/sys/user.ts +++ b/mock/sys/user.ts @@ -111,4 +111,12 @@ export default [ return resultSuccess(undefined, { message: 'Token has been destroyed' }); }, }, + { + url: '/basic-api/testRetry', + statusCode: 405, + method: 'get', + response: () => { + return resultError('Error!'); + }, + }, ] as MockMethod[]; diff --git a/src/api/sys/user.ts b/src/api/sys/user.ts index 1e883b6dd4e..f30d14f08ba 100644 --- a/src/api/sys/user.ts +++ b/src/api/sys/user.ts @@ -8,6 +8,7 @@ enum Api { Logout = '/logout', GetUserInfo = '/getUserInfo', GetPermCode = '/getPermCode', + TestRetry = '/testRetry', } /** @@ -39,3 +40,16 @@ export function getPermCode() { export function doLogout() { return defHttp.get({ url: Api.Logout }); } + +export function testRetry() { + return defHttp.get( + { url: Api.TestRetry }, + { + retryRequest: { + isOpenRetry: true, + count: 5, + waitTime: 1000, + }, + }, + ); +} diff --git a/src/locales/lang/en/routes/demo.ts b/src/locales/lang/en/routes/demo.ts index cdd554cae04..fb3bbc9f5de 100644 --- a/src/locales/lang/en/routes/demo.ts +++ b/src/locales/lang/en/routes/demo.ts @@ -92,6 +92,7 @@ export default { breadcrumb: 'Breadcrumbs', breadcrumbFlat: 'Flat Mode', breadcrumbFlatDetail: 'Flat mode details', + requestDemo: 'Retry request demo', breadcrumbChildren: 'Level mode', breadcrumbChildrenDetail: 'Level mode detail', diff --git a/src/locales/lang/zh-CN/routes/demo.ts b/src/locales/lang/zh-CN/routes/demo.ts index 96d11e48d1e..92d2ea27e5d 100644 --- a/src/locales/lang/zh-CN/routes/demo.ts +++ b/src/locales/lang/zh-CN/routes/demo.ts @@ -88,6 +88,7 @@ export default { ws: 'websocket测试', breadcrumb: '面包屑导航', breadcrumbFlat: '平级模式', + requestDemo: '测试请求重试', breadcrumbFlatDetail: '平级详情', breadcrumbChildren: '层级模式', breadcrumbChildrenDetail: '层级详情', diff --git a/src/router/routes/modules/demo/feat.ts b/src/router/routes/modules/demo/feat.ts index 7c7121d85ef..8d8a83fc935 100644 --- a/src/router/routes/modules/demo/feat.ts +++ b/src/router/routes/modules/demo/feat.ts @@ -31,6 +31,15 @@ const feat: AppRouteModule = { title: t('routes.demo.feat.ws'), }, }, + { + path: 'request', + name: 'RequestDemo', + // @ts-ignore + component: () => import('/@/views/demo/feat/request-demo/index.vue'), + meta: { + title: t('routes.demo.feat.requestDemo'), + }, + }, { path: 'session-timeout', name: 'SessionTimeout', diff --git a/src/utils/http/axios/Axios.ts b/src/utils/http/axios/Axios.ts index d8b877774ce..e3e912d6fe1 100644 --- a/src/utils/http/axios/Axios.ts +++ b/src/utils/http/axios/Axios.ts @@ -111,7 +111,10 @@ export class VAxios { // Response result interceptor error capture responseInterceptorsCatch && isFunction(responseInterceptorsCatch) && - this.axiosInstance.interceptors.response.use(undefined, responseInterceptorsCatch); + this.axiosInstance.interceptors.response.use(undefined, (error) => { + // @ts-ignore + responseInterceptorsCatch(this.axiosInstance, error); + }); } /** diff --git a/src/utils/http/axios/axiosRetry.ts b/src/utils/http/axios/axiosRetry.ts new file mode 100644 index 00000000000..d081b967635 --- /dev/null +++ b/src/utils/http/axios/axiosRetry.ts @@ -0,0 +1,28 @@ +import { AxiosError, AxiosInstance } from 'axios'; +/** + * 请求重试机制 + */ + +export class AxiosRetry { + /** + * 重试 + */ + retry(AxiosInstance: AxiosInstance, error: AxiosError) { + // @ts-ignore + const { config } = error.response; + const { waitTime, count } = config?.requestOptions?.retryRequest; + config.__retryCount = config.__retryCount || 0; + if (config.__retryCount >= count) { + return Promise.reject(error); + } + config.__retryCount += 1; + return this.delay(waitTime).then(() => AxiosInstance(config)); + } + + /** + * 延迟 + */ + private delay(waitTime: number) { + return new Promise((resolve) => setTimeout(resolve, waitTime)); + } +} diff --git a/src/utils/http/axios/axiosTransform.ts b/src/utils/http/axios/axiosTransform.ts index f6cbc80af2d..beeac6cb036 100644 --- a/src/utils/http/axios/axiosTransform.ts +++ b/src/utils/http/axios/axiosTransform.ts @@ -48,5 +48,5 @@ export abstract class AxiosTransform { /** * @description: 请求之后的拦截器错误处理 */ - responseInterceptorsCatch?: (error: Error) => void; + responseInterceptorsCatch?: (axiosInstance: AxiosResponse, error: Error) => void; } diff --git a/src/utils/http/axios/index.ts b/src/utils/http/axios/index.ts index 97dee455eec..8c5c00ea09b 100644 --- a/src/utils/http/axios/index.ts +++ b/src/utils/http/axios/index.ts @@ -17,6 +17,7 @@ import { useErrorLogStoreWithOut } from '/@/store/modules/errorLog'; import { useI18n } from '/@/hooks/web/useI18n'; import { joinTimestamp, formatRequestDate } from './helper'; import { useUserStoreWithOut } from '/@/store/modules/user'; +import { AxiosRetry } from '/@/utils/http/axios/axiosRetry'; const globSetting = useGlobSetting(); const urlPrefix = globSetting.urlPrefix; @@ -158,7 +159,7 @@ const transform: AxiosTransform = { /** * @description: 响应错误处理 */ - responseInterceptorsCatch: (error: any) => { + responseInterceptorsCatch: (axiosInstance: AxiosResponse, error: any) => { const { t } = useI18n(); const errorLogStore = useErrorLogStoreWithOut(); errorLogStore.addAjaxErrorInfo(error); @@ -189,6 +190,14 @@ const transform: AxiosTransform = { } checkStatus(error?.response?.status, msg, errorMessageMode); + + // 添加自动重试机制 保险起见 只针对GET请求 + const retryRequest = new AxiosRetry(); + const { isOpenRetry } = config.requestOptions.retryRequest; + config.method?.toUpperCase() === RequestEnum.GET && + isOpenRetry && + // @ts-ignore + retryRequest.retry(axiosInstance, error); return Promise.reject(error); }, }; @@ -234,6 +243,11 @@ function createAxios(opt?: Partial) { ignoreCancelToken: true, // 是否携带token withToken: true, + retryRequest: { + isOpenRetry: true, + count: 5, + waitTime: 100, + }, }, }, opt || {}, diff --git a/src/views/demo/feat/request-demo/index.vue b/src/views/demo/feat/request-demo/index.vue new file mode 100644 index 00000000000..b1da5227f58 --- /dev/null +++ b/src/views/demo/feat/request-demo/index.vue @@ -0,0 +1,23 @@ + + + + diff --git a/types/axios.d.ts b/types/axios.d.ts index e60187f9fe9..b18af05af89 100644 --- a/types/axios.d.ts +++ b/types/axios.d.ts @@ -23,8 +23,15 @@ export interface RequestOptions { ignoreCancelToken?: boolean; // Whether to send token in header withToken?: boolean; + // 请求重试机制 + retryRequest?: RetryRequest; } +export interface RetryRequest { + isOpenRetry: boolean; + count: number; + waitTime: number; +} export interface Result { code: number; type: 'success' | 'error' | 'warning';