Axios.ts 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. import type { AxiosRequestConfig, AxiosInstance, AxiosResponse, AxiosError } from 'axios';
  2. import type { RequestOptions, Result, UploadFileParams } from '/#/axios';
  3. import type { CreateAxiosOptions } from './axiosTransform';
  4. import axios from 'axios';
  5. import qs from 'qs';
  6. import { AxiosCanceler } from './axiosCancel';
  7. import { isFunction } from '/@/utils/is';
  8. import { cloneDeep } from 'lodash-es';
  9. import { ContentTypeEnum, RequestEnum } from '/@/enums/httpEnum';
  10. export * from './axiosTransform';
  11. /**
  12. * @description: axios module
  13. */
  14. export class VAxios {
  15. private axiosInstance: AxiosInstance;
  16. private readonly options: CreateAxiosOptions;
  17. constructor(options: CreateAxiosOptions) {
  18. this.options = options;
  19. this.axiosInstance = axios.create(options);
  20. this.setupInterceptors();
  21. }
  22. /**
  23. * @description: Create axios instance
  24. */
  25. private createAxios(config: CreateAxiosOptions): void {
  26. this.axiosInstance = axios.create(config);
  27. }
  28. private getTransform() {
  29. const { transform } = this.options;
  30. return transform;
  31. }
  32. getAxios(): AxiosInstance {
  33. return this.axiosInstance;
  34. }
  35. /**
  36. * @description: Reconfigure axios
  37. */
  38. configAxios(config: CreateAxiosOptions) {
  39. if (!this.axiosInstance) {
  40. return;
  41. }
  42. this.createAxios(config);
  43. }
  44. /**
  45. * @description: Set general header
  46. */
  47. setHeader(headers: any): void {
  48. if (!this.axiosInstance) {
  49. return;
  50. }
  51. Object.assign(this.axiosInstance.defaults.headers, headers);
  52. }
  53. /**
  54. * @description: Interceptor configuration 拦截器配置
  55. */
  56. private setupInterceptors() {
  57. const transform = this.getTransform();
  58. if (!transform) {
  59. return;
  60. }
  61. const {
  62. requestInterceptors,
  63. requestInterceptorsCatch,
  64. responseInterceptors,
  65. responseInterceptorsCatch,
  66. } = transform;
  67. const axiosCanceler = new AxiosCanceler();
  68. // Request interceptor configuration processing
  69. this.axiosInstance.interceptors.request.use((config: AxiosRequestConfig) => {
  70. // If cancel repeat request is turned on, then cancel repeat request is prohibited
  71. // @ts-ignore
  72. const { ignoreCancelToken } = config.requestOptions;
  73. const ignoreCancel =
  74. ignoreCancelToken !== undefined
  75. ? ignoreCancelToken
  76. : this.options.requestOptions?.ignoreCancelToken;
  77. !ignoreCancel && axiosCanceler.addPending(config);
  78. if (requestInterceptors && isFunction(requestInterceptors)) {
  79. config = requestInterceptors(config, this.options);
  80. }
  81. return config;
  82. }, undefined);
  83. // Request interceptor error capture
  84. requestInterceptorsCatch &&
  85. isFunction(requestInterceptorsCatch) &&
  86. this.axiosInstance.interceptors.request.use(undefined, requestInterceptorsCatch);
  87. // Response result interceptor processing
  88. this.axiosInstance.interceptors.response.use((res: AxiosResponse<any>) => {
  89. res && axiosCanceler.removePending(res.config);
  90. if (responseInterceptors && isFunction(responseInterceptors)) {
  91. res = responseInterceptors(res);
  92. }
  93. return res;
  94. }, undefined);
  95. // Response result interceptor error capture
  96. responseInterceptorsCatch &&
  97. isFunction(responseInterceptorsCatch) &&
  98. this.axiosInstance.interceptors.response.use(undefined, (error) => {
  99. // @ts-ignore
  100. return responseInterceptorsCatch(this.axiosInstance, error);
  101. });
  102. }
  103. /**
  104. * @description: File Upload
  105. */
  106. uploadFile<T = any>(config: AxiosRequestConfig, params: UploadFileParams) {
  107. const formData = new window.FormData();
  108. const customFilename = params.name || 'file';
  109. if (params.filename) {
  110. formData.append(customFilename, params.file, params.filename);
  111. } else {
  112. formData.append(customFilename, params.file);
  113. }
  114. if (params.data) {
  115. Object.keys(params.data).forEach((key) => {
  116. const value = params.data![key];
  117. if (Array.isArray(value)) {
  118. value.forEach((item) => {
  119. formData.append(`${key}[]`, item);
  120. });
  121. return;
  122. }
  123. formData.append(key, params.data![key]);
  124. });
  125. }
  126. return this.axiosInstance.request<T>({
  127. ...config,
  128. method: 'POST',
  129. data: formData,
  130. headers: {
  131. 'Content-type': ContentTypeEnum.FORM_DATA,
  132. // @ts-ignore
  133. ignoreCancelToken: true,
  134. },
  135. });
  136. }
  137. // support form-data
  138. supportFormData(config: AxiosRequestConfig) {
  139. const headers = config.headers || this.options.headers;
  140. const contentType = headers?.['Content-Type'] || headers?.['content-type'];
  141. if (
  142. contentType !== ContentTypeEnum.FORM_URLENCODED ||
  143. !Reflect.has(config, 'data') ||
  144. config.method?.toUpperCase() === RequestEnum.GET
  145. ) {
  146. return config;
  147. }
  148. return {
  149. ...config,
  150. data: qs.stringify(config.data, { arrayFormat: 'brackets' }),
  151. };
  152. }
  153. get<T = any>(config: AxiosRequestConfig, options?: RequestOptions): Promise<T> {
  154. return this.request({ ...config, method: 'GET' }, options);
  155. }
  156. post<T = any>(config: AxiosRequestConfig, options?: RequestOptions): Promise<T> {
  157. return this.request({ ...config, method: 'POST' }, options);
  158. }
  159. put<T = any>(config: AxiosRequestConfig, options?: RequestOptions): Promise<T> {
  160. return this.request({ ...config, method: 'PUT' }, options);
  161. }
  162. delete<T = any>(config: AxiosRequestConfig, options?: RequestOptions): Promise<T> {
  163. return this.request({ ...config, method: 'DELETE' }, options);
  164. }
  165. request<T = any>(config: AxiosRequestConfig, options?: RequestOptions): Promise<T> {
  166. let conf: CreateAxiosOptions = cloneDeep(config);
  167. // cancelToken 如果被深拷贝,会导致最外层无法使用cancel方法来取消请求
  168. if (config.cancelToken) {
  169. conf.cancelToken = config.cancelToken;
  170. }
  171. const transform = this.getTransform();
  172. const { requestOptions } = this.options;
  173. const opt: RequestOptions = Object.assign({}, requestOptions, options);
  174. const { beforeRequestHook, requestCatchHook, transformResponseHook } = transform || {};
  175. if (beforeRequestHook && isFunction(beforeRequestHook)) {
  176. conf = beforeRequestHook(conf, opt);
  177. }
  178. conf.requestOptions = opt;
  179. conf = this.supportFormData(conf);
  180. return new Promise((resolve, reject) => {
  181. this.axiosInstance
  182. .request<any, AxiosResponse<Result>>(conf)
  183. .then((res: AxiosResponse<Result>) => {
  184. if (transformResponseHook && isFunction(transformResponseHook)) {
  185. try {
  186. const ret = transformResponseHook(res, opt);
  187. resolve(ret);
  188. } catch (err) {
  189. reject(err || new Error('request error!'));
  190. }
  191. return;
  192. }
  193. resolve(res as unknown as Promise<T>);
  194. })
  195. .catch((e: Error | AxiosError) => {
  196. if (requestCatchHook && isFunction(requestCatchHook)) {
  197. reject(requestCatchHook(e, opt));
  198. return;
  199. }
  200. if (axios.isAxiosError(e)) {
  201. // rewrite error message from axios in here
  202. }
  203. reject(e);
  204. });
  205. });
  206. }
  207. }