Axios.ts 6.9 KB

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