vben
2021-09-09 996f2f3c228ce887e81f3d263452b0cd2d7716be
提交 | 用户 | age
2f6253 1 // axios配置  可自行根据项目进行更改,只需更改该文件即可,其他文件可以不动
2 // The axios configuration can be changed according to the project, just change the file, other files can be left unchanged
3
4 import type { AxiosResponse } from 'axios';
b6d5b0 5 import type { RequestOptions, Result } from '/#/axios';
b7ce74 6 import type { AxiosTransform, CreateAxiosOptions } from './axiosTransform';
V 7 import { VAxios } from './Axios';
2f6253 8 import { checkStatus } from './checkStatus';
ba068b 9 import { useGlobSetting } from '/@/hooks/setting';
2f6253 10 import { useMessage } from '/@/hooks/web/useMessage';
11 import { RequestEnum, ResultEnum, ContentTypeEnum } from '/@/enums/httpEnum';
12 import { isString } from '/@/utils/is';
b7ce74 13 import { getToken } from '/@/utils/auth';
2f6253 14 import { setObjToUrlParams, deepMerge } from '/@/utils';
215d8b 15 import { useErrorLogStoreWithOut } from '/@/store/modules/errorLog';
190112 16 import { useI18n } from '/@/hooks/web/useI18n';
50f94b 17 import { joinTimestamp, formatRequestDate } from './helper';
2f6253 18
737b1b 19 const globSetting = useGlobSetting();
50f94b 20 const urlPrefix = globSetting.urlPrefix;
2f6253 21 const { createMessage, createErrorModal } = useMessage();
22
23 /**
24  * @description: 数据处理,方便区分多种处理方式
25  */
26 const transform: AxiosTransform = {
27   /**
b218f1 28    * @description: 处理请求数据。如果数据不是预期格式,可直接抛出错误
2f6253 29    */
a821d9 30   transformRequestHook: (res: AxiosResponse<Result>, options: RequestOptions) => {
962f90 31     const { t } = useI18n();
b6d5b0 32     const { isTransformResponse, isReturnNativeResponse } = options;
56d8af 33     // 是否返回原生响应头 比如:需要获取响应头时使用该属性
Z 34     if (isReturnNativeResponse) {
35       return res;
36     }
2f6253 37     // 不进行任何处理,直接返回
38     // 用于页面代码可能需要直接获取code,data,message这些信息时开启
b6d5b0 39     if (!isTransformResponse) {
2f6253 40       return res.data;
41     }
42     // 错误的时候返回
43
44     const { data } = res;
45     if (!data) {
46       // return '[HTTP] Request has no return value';
b218f1 47       throw new Error(t('sys.api.apiRequestFailed'));
2f6253 48     }
49     //  这里 code,result,message为 后台统一的字段,需要在 types.ts内修改为项目自己的接口返回格式
50     const { code, result, message } = data;
51
52     // 这里逻辑可以根据项目进行修改
53     const hasSuccess = data && Reflect.has(data, 'code') && code === ResultEnum.SUCCESS;
b69dcd 54     if (hasSuccess) {
e83cb0 55       return result;
2f6253 56     }
b69dcd 57
58     // 在此处根据自己项目的实际情况对不同的code执行不同的操作
59     // 如果不希望中断当前请求,请return数据,否则直接抛出异常即可
49b66e 60     let timeoutMsg = '';
b69dcd 61     switch (code) {
62       case ResultEnum.TIMEOUT:
49b66e 63         timeoutMsg = t('sys.api.timeoutMessage');
49f39d 64         break;
b69dcd 65       default:
66         if (message) {
49b66e 67           timeoutMsg = message;
b69dcd 68         }
2f6253 69     }
49b66e 70
X 71     // errorMessageMode=‘modal’的时候会显示modal错误弹窗,而不是消息提示,用于一些比较重要的错误
72     // errorMessageMode='none' 一般是调用时明确表示不希望自动弹出错误提示
73     if (options.errorMessageMode === 'modal') {
74       createErrorModal({ title: t('sys.api.errorTip'), content: timeoutMsg });
75     } else if (options.errorMessageMode === 'message') {
76       createMessage.error(timeoutMsg);
77     }
78
79     throw new Error(timeoutMsg || t('sys.api.apiRequestFailed'));
2f6253 80   },
81
82   // 请求之前处理config
83   beforeRequestHook: (config, options) => {
996f2f 84     const { apiUrl, joinPrefix, joinParamsToUrl, formatDate, joinTime = true, urlPrefix } = options;
2f6253 85
86     if (joinPrefix) {
50f94b 87       config.url = `${urlPrefix}${config.url}`;
2f6253 88     }
89
90     if (apiUrl && isString(apiUrl)) {
91       config.url = `${apiUrl}${config.url}`;
92     }
ac1a36 93     const params = config.params || {};
49f39d 94     const data = config.data || false;
95     formatDate && data && !isString(data) && formatRequestDate(data);
6b3195 96     if (config.method?.toUpperCase() === RequestEnum.GET) {
ac1a36 97       if (!isString(params)) {
c96002 98         // 给 get 请求加上时间戳参数,避免从缓存中拿数据。
50f94b 99         config.params = Object.assign(params || {}, joinTimestamp(joinTime, false));
2f6253 100       } else {
101         // 兼容restful风格
50f94b 102         config.url = config.url + params + `${joinTimestamp(joinTime, true)}`;
34c09f 103         config.params = undefined;
2f6253 104       }
105     } else {
ac1a36 106       if (!isString(params)) {
V 107         formatDate && formatRequestDate(params);
49f39d 108         if (Reflect.has(config, 'data') && config.data && Object.keys(config.data).length > 0) {
109           config.data = data;
110           config.params = params;
111         } else {
112           // 非GET请求如果没有提供data,则将params视为data
113           config.data = params;
114           config.params = undefined;
115         }
2f6253 116         if (joinParamsToUrl) {
49f39d 117           config.url = setObjToUrlParams(
118             config.url as string,
56a966 119             Object.assign({}, config.params, config.data),
49f39d 120           );
2f6253 121         }
122       } else {
123         // 兼容restful风格
ac1a36 124         config.url = config.url + params;
34c09f 125         config.params = undefined;
2f6253 126       }
127     }
128     return config;
129   },
130
131   /**
132    * @description: 请求拦截器处理
133    */
b6d5b0 134   requestInterceptors: (config, options) => {
2f6253 135     // 请求之前处理config
b7ce74 136     const token = getToken();
d509e8 137     if (token && (config as Recordable)?.requestOptions?.withToken !== false) {
2f6253 138       // jwt token
b6d5b0 139       config.headers.Authorization = options.authenticationScheme
V 140         ? `${options.authenticationScheme} ${token}`
141         : token;
2f6253 142     }
143     return config;
144   },
145
146   /**
49b66e 147    * @description: 响应拦截器处理
X 148    */
149   responseInterceptors: (res: AxiosResponse<any>) => {
150     return res;
151   },
152
153   /**
2f6253 154    * @description: 响应错误处理
155    */
156   responseInterceptorsCatch: (error: any) => {
962f90 157     const { t } = useI18n();
215d8b 158     const errorLogStore = useErrorLogStoreWithOut();
V 159     errorLogStore.addAjaxErrorInfo(error);
49b66e 160     const { response, code, message, config } = error || {};
X 161     const errorMessageMode = config?.requestOptions?.errorMessageMode || 'none';
de5bf7 162     const msg: string = response?.data?.error?.message ?? '';
V 163     const err: string = error?.toString?.() ?? '';
49b66e 164     let errMessage = '';
X 165
2f6253 166     try {
167       if (code === 'ECONNABORTED' && message.indexOf('timeout') !== -1) {
49b66e 168         errMessage = t('sys.api.apiTimeoutMessage');
2f6253 169       }
6b3195 170       if (err?.includes('Network Error')) {
49b66e 171         errMessage = t('sys.api.networkExceptionMsg');
X 172       }
173
174       if (errMessage) {
175         if (errorMessageMode === 'modal') {
176           createErrorModal({ title: t('sys.api.errorTip'), content: errMessage });
177         } else if (errorMessageMode === 'message') {
178           createMessage.error(errMessage);
179         }
180         return Promise.reject(error);
2f6253 181       }
182     } catch (error) {
183       throw new Error(error);
184     }
49b66e 185
X 186     checkStatus(error?.response?.status, msg, errorMessageMode);
661db0 187     return Promise.reject(error);
2f6253 188   },
189 };
190
191 function createAxios(opt?: Partial<CreateAxiosOptions>) {
192   return new VAxios(
193     deepMerge(
194       {
b6d5b0 195         // See https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication#authentication_schemes
V 196         // authentication schemes,e.g: Bearer
197         // authenticationScheme: 'Bearer',
198         authenticationScheme: '',
2f6253 199         timeout: 10 * 1000,
200         // 基础接口地址
61d4ef 201         // baseURL: globSetting.apiUrl,
996f2f 202
2f6253 203         headers: { 'Content-Type': ContentTypeEnum.JSON },
f646e3 204         // 如果是form-data格式
V 205         // headers: { 'Content-Type': ContentTypeEnum.FORM_URLENCODED },
2f6253 206         // 数据处理方式
207         transform,
208         // 配置项,下面的选项都可以在独立的接口请求中覆盖
209         requestOptions: {
210           // 默认将prefix 添加到url
211           joinPrefix: true,
56d8af 212           // 是否返回原生响应头 比如:需要获取响应头时使用该属性
Z 213           isReturnNativeResponse: false,
2f6253 214           // 需要对返回数据进行处理
b6d5b0 215           isTransformResponse: true,
2f6253 216           // post请求的时候添加参数到url
217           joinParamsToUrl: false,
218           // 格式化提交参数时间
219           formatDate: true,
220           // 消息提示类型
4ce1d5 221           errorMessageMode: 'message',
2f6253 222           // 接口地址
223           apiUrl: globSetting.apiUrl,
7df9b5 224           // 接口拼接地址
L 225           urlPrefix: urlPrefix,
f646e3 226           //  是否加入时间戳
V 227           joinTime: true,
3b8ca4 228           // 忽略重复请求
V 229           ignoreCancelToken: true,
c99cf5 230           // 是否携带token
231           withToken: true,
2f6253 232         },
233       },
56a966 234       opt || {},
V 235     ),
2f6253 236   );
237 }
238 export const defHttp = createAxios();
239
240 // other api url
241 // export const otherHttp = createAxios({
242 //   requestOptions: {
243 //     apiUrl: 'xxx',
7df9b5 244 //     urlPrefix: 'xxx',
2f6253 245 //   },
246 // });