Axios.ts 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  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 } 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. // @ts-ignore
  73. const { ignoreCancelToken } = config.requestOptions;
  74. const ignoreCancel =
  75. ignoreCancelToken !== undefined
  76. ? ignoreCancelToken
  77. : this.options.requestOptions?.ignoreCancelToken;
  78. !ignoreCancel && axiosCanceler.addPending(config);
  79. if (requestInterceptors && isFunction(requestInterceptors)) {
  80. config = requestInterceptors(config, this.options);
  81. }
  82. return config;
  83. }, undefined);
  84. // Request interceptor error capture
  85. requestInterceptorsCatch &&
  86. isFunction(requestInterceptorsCatch) &&
  87. this.axiosInstance.interceptors.request.use(undefined, requestInterceptorsCatch);
  88. // Response result interceptor processing
  89. this.axiosInstance.interceptors.response.use((res: AxiosResponse<any>) => {
  90. res && axiosCanceler.removePending(res.config);
  91. if (responseInterceptors && isFunction(responseInterceptors)) {
  92. res = responseInterceptors(res);
  93. }
  94. return res;
  95. }, undefined);
  96. // Response result interceptor error capture
  97. responseInterceptorsCatch &&
  98. isFunction(responseInterceptorsCatch) &&
  99. this.axiosInstance.interceptors.response.use(undefined, (error) => {
  100. // @ts-ignore
  101. return responseInterceptorsCatch(this.axiosInstance, error);
  102. });
  103. }
  104. /**
  105. * @description: File Upload
  106. */
  107. uploadFile<T = any>(config: AxiosRequestConfig, params: UploadFileParams) {
  108. const formData = new window.FormData();
  109. const customFilename = params.name || 'file';
  110. if (params.filename) {
  111. formData.append(customFilename, params.file, params.filename);
  112. } else {
  113. formData.append(customFilename, params.file);
  114. }
  115. if (params.data) {
  116. Object.keys(params.data).forEach((key) => {
  117. const value = params.data![key];
  118. if (Array.isArray(value)) {
  119. value.forEach((item) => {
  120. formData.append(`${key}[]`, item);
  121. });
  122. return;
  123. }
  124. formData.append(key, params.data![key]);
  125. });
  126. }
  127. return this.axiosInstance.request<T>({
  128. ...config,
  129. method: 'POST',
  130. data: formData,
  131. headers: {
  132. 'Content-type': ContentTypeEnum.FORM_DATA,
  133. // @ts-ignore
  134. ignoreCancelToken: true,
  135. },
  136. });
  137. }
  138. // support form-data
  139. supportFormData(config: AxiosRequestConfig) {
  140. const headers = config.headers || this.options.headers;
  141. const contentType = headers?.['Content-Type'] || headers?.['content-type'];
  142. if (
  143. contentType !== ContentTypeEnum.FORM_URLENCODED ||
  144. !Reflect.has(config, 'data') ||
  145. config.method?.toUpperCase() === RequestEnum.GET
  146. ) {
  147. return config;
  148. }
  149. return {
  150. ...config,
  151. data: qs.stringify(config.data, { arrayFormat: 'brackets' }),
  152. };
  153. }
  154. get<T = any>(config: AxiosRequestConfig, options?: RequestOptions): Promise<T> {
  155. return this.request({ ...config, method: 'GET' }, options);
  156. }
  157. post<T = any>(config: AxiosRequestConfig, options?: RequestOptions): Promise<T> {
  158. return this.request({ ...config, method: 'POST' }, options);
  159. }
  160. put<T = any>(config: AxiosRequestConfig, options?: RequestOptions): Promise<T> {
  161. return this.request({ ...config, method: 'PUT' }, options);
  162. }
  163. delete<T = any>(config: AxiosRequestConfig, options?: RequestOptions): Promise<T> {
  164. return this.request({ ...config, method: 'DELETE' }, options);
  165. }
  166. request<T = any>(config: AxiosRequestConfig, options?: RequestOptions): Promise<T> {
  167. let conf: CreateAxiosOptions = cloneDeep(config);
  168. const transform = this.getTransform();
  169. const { requestOptions } = this.options;
  170. const opt: RequestOptions = Object.assign({}, requestOptions, options);
  171. const { beforeRequestHook, requestCatchHook, transformRequestHook } = transform || {};
  172. if (beforeRequestHook && isFunction(beforeRequestHook)) {
  173. conf = beforeRequestHook(conf, opt);
  174. }
  175. conf.requestOptions = opt;
  176. conf = this.supportFormData(conf);
  177. return new Promise((resolve, reject) => {
  178. this.axiosInstance
  179. .request<any, AxiosResponse<Result>>(conf)
  180. .then((res: AxiosResponse<Result>) => {
  181. if (transformRequestHook && isFunction(transformRequestHook)) {
  182. try {
  183. const ret = transformRequestHook(res, opt);
  184. resolve(ret);
  185. } catch (err) {
  186. reject(err || new Error('request error!'));
  187. }
  188. return;
  189. }
  190. resolve(res as unknown as Promise<T>);
  191. })
  192. .catch((e: Error | AxiosError) => {
  193. if (requestCatchHook && isFunction(requestCatchHook)) {
  194. reject(requestCatchHook(e, opt));
  195. return;
  196. }
  197. if (axios.isAxiosError(e)) {
  198. // rewrite error message from axios in here
  199. }
  200. reject(e);
  201. });
  202. });
  203. }
  204. }