From 136cbb1e3bc056c88cfa21fca612d3ab72b4d119 Mon Sep 17 00:00:00 2001
From: Captain <49203535+MssText@users.noreply.github.com>
Date: 星期六, 19 三月 2022 00:07:34 +0800
Subject: [PATCH] feat: add request retry (#1553)

---
 src/utils/http/axios/Axios.ts |  147 ++++++++++++++++++++++++++++++++++++------------
 1 files changed, 110 insertions(+), 37 deletions(-)

diff --git a/src/utils/http/axios/Axios.ts b/src/utils/http/axios/Axios.ts
index 1da69da..e3e912d 100644
--- a/src/utils/http/axios/Axios.ts
+++ b/src/utils/http/axios/Axios.ts
@@ -1,21 +1,22 @@
-import type { AxiosRequestConfig, AxiosInstance, AxiosResponse } from 'axios';
-
+import type { AxiosRequestConfig, AxiosInstance, AxiosResponse, AxiosError } from 'axios';
+import type { RequestOptions, Result, UploadFileParams } from '/#/axios';
+import type { CreateAxiosOptions } from './axiosTransform';
 import axios from 'axios';
+import qs from 'qs';
 import { AxiosCanceler } from './axiosCancel';
 import { isFunction } from '/@/utils/is';
 import { cloneDeep } from 'lodash-es';
-
-import { RequestOptions, CreateAxiosOptions, Result } from './types';
 import { ContentTypeEnum } from '/@/enums/httpEnum';
+import { RequestEnum } from '/@/enums/httpEnum';
 
 export * from './axiosTransform';
 
 /**
- * @description:  axios妯″潡
+ * @description:  axios module
  */
 export class VAxios {
   private axiosInstance: AxiosInstance;
-  private options: CreateAxiosOptions;
+  private readonly options: CreateAxiosOptions;
 
   constructor(options: CreateAxiosOptions) {
     this.options = options;
@@ -24,7 +25,7 @@
   }
 
   /**
-   * @description:  鍒涘缓axios瀹炰緥
+   * @description:  Create axios instance
    */
   private createAxios(config: CreateAxiosOptions): void {
     this.axiosInstance = axios.create(config);
@@ -40,7 +41,7 @@
   }
 
   /**
-   * @description: 閲嶆柊閰嶇疆axios
+   * @description: Reconfigure axios
    */
   configAxios(config: CreateAxiosOptions) {
     if (!this.axiosInstance) {
@@ -50,7 +51,7 @@
   }
 
   /**
-   * @description: 璁剧疆閫氱敤header
+   * @description: Set general header
    */
   setHeader(headers: any): void {
     if (!this.axiosInstance) {
@@ -60,7 +61,7 @@
   }
 
   /**
-   * @description: 鎷︽埅鍣ㄩ厤缃�
+   * @description: Interceptor configuration
    */
   private setupInterceptors() {
     const transform = this.getTransform();
@@ -76,22 +77,29 @@
 
     const axiosCanceler = new AxiosCanceler();
 
-    // 璇锋眰鎷︽埅鍣ㄩ厤缃鐞�
+    // Request interceptor configuration processing
     this.axiosInstance.interceptors.request.use((config: AxiosRequestConfig) => {
-      const { headers: { ignoreCancelToken } = { ignoreCancelToken: false } } = config;
-      !ignoreCancelToken && axiosCanceler.addPending(config);
+      // If cancel repeat request is turned on, then cancel repeat request is prohibited
+      // @ts-ignore
+      const { ignoreCancelToken } = config.requestOptions;
+      const ignoreCancel =
+        ignoreCancelToken !== undefined
+          ? ignoreCancelToken
+          : this.options.requestOptions?.ignoreCancelToken;
+
+      !ignoreCancel && axiosCanceler.addPending(config);
       if (requestInterceptors && isFunction(requestInterceptors)) {
-        config = requestInterceptors(config);
+        config = requestInterceptors(config, this.options);
       }
       return config;
     }, undefined);
 
-    // 璇锋眰鎷︽埅鍣ㄩ敊璇崟鑾�
+    // Request interceptor error capture
     requestInterceptorsCatch &&
       isFunction(requestInterceptorsCatch) &&
       this.axiosInstance.interceptors.request.use(undefined, requestInterceptorsCatch);
 
-    // 鍝嶅簲缁撴灉鎷︽埅鍣ㄥ鐞�
+    // Response result interceptor processing
     this.axiosInstance.interceptors.response.use((res: AxiosResponse<any>) => {
       res && axiosCanceler.removePending(res.config);
       if (responseInterceptors && isFunction(responseInterceptors)) {
@@ -100,63 +108,128 @@
       return res;
     }, undefined);
 
-    // 鍝嶅簲缁撴灉鎷︽埅鍣ㄩ敊璇崟鑾�
+    // 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);
+      });
   }
 
   /**
-   * @description:  鏂囦欢涓婁紶
+   * @description:  File Upload
    */
-  uploadFiles(config: AxiosRequestConfig, params: File[]) {
-    const formData = new FormData();
+  uploadFile<T = any>(config: AxiosRequestConfig, params: UploadFileParams) {
+    const formData = new window.FormData();
+    const customFilename = params.name || 'file';
 
-    Object.keys(params).forEach((key) => {
-      formData.append(key, params[key as any]);
-    });
+    if (params.filename) {
+      formData.append(customFilename, params.file, params.filename);
+    } else {
+      formData.append(customFilename, params.file);
+    }
 
-    return this.request({
+    if (params.data) {
+      Object.keys(params.data).forEach((key) => {
+        const value = params.data![key];
+        if (Array.isArray(value)) {
+          value.forEach((item) => {
+            formData.append(`${key}[]`, item);
+          });
+          return;
+        }
+
+        formData.append(key, params.data![key]);
+      });
+    }
+
+    return this.axiosInstance.request<T>({
       ...config,
       method: 'POST',
       data: formData,
       headers: {
         'Content-type': ContentTypeEnum.FORM_DATA,
+        // @ts-ignore
+        ignoreCancelToken: true,
       },
     });
   }
 
-  /**
-   * @description:   璇锋眰鏂规硶
-   */
+  // support form-data
+  supportFormData(config: AxiosRequestConfig) {
+    const headers = config.headers || this.options.headers;
+    const contentType = headers?.['Content-Type'] || headers?.['content-type'];
+
+    if (
+      contentType !== ContentTypeEnum.FORM_URLENCODED ||
+      !Reflect.has(config, 'data') ||
+      config.method?.toUpperCase() === RequestEnum.GET
+    ) {
+      return config;
+    }
+
+    return {
+      ...config,
+      data: qs.stringify(config.data, { arrayFormat: 'brackets' }),
+    };
+  }
+
+  get<T = any>(config: AxiosRequestConfig, options?: RequestOptions): Promise<T> {
+    return this.request({ ...config, method: 'GET' }, options);
+  }
+
+  post<T = any>(config: AxiosRequestConfig, options?: RequestOptions): Promise<T> {
+    return this.request({ ...config, method: 'POST' }, options);
+  }
+
+  put<T = any>(config: AxiosRequestConfig, options?: RequestOptions): Promise<T> {
+    return this.request({ ...config, method: 'PUT' }, options);
+  }
+
+  delete<T = any>(config: AxiosRequestConfig, options?: RequestOptions): Promise<T> {
+    return this.request({ ...config, method: 'DELETE' }, options);
+  }
+
   request<T = any>(config: AxiosRequestConfig, options?: RequestOptions): Promise<T> {
-    let conf: AxiosRequestConfig = cloneDeep(config);
+    let conf: CreateAxiosOptions = cloneDeep(config);
     const transform = this.getTransform();
 
     const { requestOptions } = this.options;
 
     const opt: RequestOptions = Object.assign({}, requestOptions, options);
 
-    const { beforeRequestHook, requestCatch, transformRequestData } = transform || {};
+    const { beforeRequestHook, requestCatchHook, transformRequestHook } = transform || {};
     if (beforeRequestHook && isFunction(beforeRequestHook)) {
       conf = beforeRequestHook(conf, opt);
     }
+    conf.requestOptions = opt;
+
+    conf = this.supportFormData(conf);
+
     return new Promise((resolve, reject) => {
       this.axiosInstance
         .request<any, AxiosResponse<Result>>(conf)
         .then((res: AxiosResponse<Result>) => {
-          if (transformRequestData && isFunction(transformRequestData)) {
-            const ret = transformRequestData(res, opt);
-            ret !== undefined ? resolve(ret) : reject(new Error('request error!'));
+          if (transformRequestHook && isFunction(transformRequestHook)) {
+            try {
+              const ret = transformRequestHook(res, opt);
+              resolve(ret);
+            } catch (err) {
+              reject(err || new Error('request error!'));
+            }
             return;
           }
-          resolve((res as unknown) as Promise<T>);
+          resolve(res as unknown as Promise<T>);
         })
-        .catch((e: Error) => {
-          if (requestCatch && isFunction(requestCatch)) {
-            reject(requestCatch(e));
+        .catch((e: Error | AxiosError) => {
+          if (requestCatchHook && isFunction(requestCatchHook)) {
+            reject(requestCatchHook(e, opt));
             return;
           }
+          if (axios.isAxiosError(e)) {
+            // rewrite error message from axios in here
+          }
           reject(e);
         });
     });

--
Gitblit v1.8.0