Axios.ts 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  1. import type { AxiosRequestConfig, AxiosInstance, AxiosResponse, AxiosError } from 'axios'
  2. import type { RequestOptions, RequestResult, 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 } from 'lodash-es'
  9. import { ContentTypeEnum } from '@/enums/http.enum'
  10. import { RequestEnum } from '@/enums/http.enum'
  11. import { downloadByData } from '@/utils/filt'
  12. export * from './axiosTransform'
  13. /**
  14. * @description: axios module
  15. */
  16. export class VAxios {
  17. private axiosInstance: AxiosInstance
  18. private readonly options: CreateAxiosOptions
  19. constructor(options: CreateAxiosOptions) {
  20. this.options = options
  21. this.axiosInstance = axios.create(options)
  22. this.setupInterceptors()
  23. }
  24. /**
  25. * @description: Create axios instance
  26. */
  27. private createAxios(config: CreateAxiosOptions): void {
  28. this.axiosInstance = axios.create(config)
  29. }
  30. private getTransform() {
  31. const { transform } = this.options
  32. return transform
  33. }
  34. getAxios(): AxiosInstance {
  35. return this.axiosInstance
  36. }
  37. /**
  38. * @description: Reconfigure axios
  39. */
  40. configAxios(config: CreateAxiosOptions) {
  41. if (!this.axiosInstance) {
  42. return
  43. }
  44. this.createAxios(config)
  45. }
  46. /**
  47. * @description: Set general header
  48. */
  49. setHeader(headers: any): void {
  50. if (!this.axiosInstance) {
  51. return
  52. }
  53. Object.assign(this.axiosInstance.defaults.headers, headers)
  54. }
  55. /**
  56. * @description: Interceptor configuration
  57. */
  58. private setupInterceptors() {
  59. const transform = this.getTransform()
  60. if (!transform) {
  61. return
  62. }
  63. const {
  64. requestInterceptors,
  65. requestInterceptorsCatch,
  66. responseInterceptors,
  67. responseInterceptorsCatch
  68. } = transform
  69. const axiosCanceler = new AxiosCanceler()
  70. // Request interceptor configuration processing
  71. this.axiosInstance.interceptors.request.use((config: AxiosRequestConfig) => {
  72. // If cancel repeat request is turned on, then cancel repeat request is prohibited
  73. const {
  74. // @ts-ignore
  75. headers: { ignoreCancelToken }
  76. } = config
  77. const ignoreCancel =
  78. ignoreCancelToken !== undefined
  79. ? ignoreCancelToken
  80. : this.options.requestOptions?.ignoreCancelToken
  81. !ignoreCancel && axiosCanceler.addPending(config)
  82. if (requestInterceptors && isFunction(requestInterceptors)) {
  83. config = requestInterceptors(config, this.options)
  84. }
  85. return config
  86. }, undefined)
  87. // Request interceptor error capture
  88. requestInterceptorsCatch &&
  89. isFunction(requestInterceptorsCatch) &&
  90. this.axiosInstance.interceptors.request.use(undefined, requestInterceptorsCatch)
  91. // Response result interceptor processing
  92. this.axiosInstance.interceptors.response.use((res: AxiosResponse<any>) => {
  93. res && axiosCanceler.removePending(res.config)
  94. if (responseInterceptors && isFunction(responseInterceptors)) {
  95. res = responseInterceptors(res)
  96. }
  97. return res
  98. }, undefined)
  99. // Response result interceptor error capture
  100. responseInterceptorsCatch &&
  101. isFunction(responseInterceptorsCatch) &&
  102. this.axiosInstance.interceptors.response.use(undefined, responseInterceptorsCatch)
  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: {
  131. file: formData
  132. },
  133. headers: {
  134. 'Content-type': ContentTypeEnum.FORM_DATA,
  135. // @ts-ignore
  136. ignoreCancelToken: true
  137. }
  138. })
  139. }
  140. // support form-data
  141. supportFormData(config: AxiosRequestConfig) {
  142. const headers = config.headers || this.options.headers
  143. const contentType = headers?.['Content-Type'] || headers?.['content-type']
  144. if (
  145. contentType !== ContentTypeEnum.FORM_URLENCODED ||
  146. !Reflect.has(config, 'data') ||
  147. config.method?.toUpperCase() === RequestEnum.GET
  148. ) {
  149. return config
  150. }
  151. return {
  152. ...config,
  153. data: qs.stringify(config.data, { arrayFormat: 'brackets' })
  154. }
  155. }
  156. get<T = any>(config: AxiosRequestConfig, options?: RequestOptions): Promise<T> {
  157. return this.request({ ...config, method: 'GET' }, options)
  158. }
  159. post<T = any>(config: AxiosRequestConfig, options?: RequestOptions): Promise<T> {
  160. return this.request({ ...config, method: 'POST' }, options)
  161. }
  162. put<T = any>(config: AxiosRequestConfig, options?: RequestOptions): Promise<T> {
  163. return this.request({ ...config, method: 'PUT' }, options)
  164. }
  165. delete<T = any>(config: AxiosRequestConfig, options?: RequestOptions): Promise<T> {
  166. return this.request({ ...config, method: 'DELETE' }, options)
  167. }
  168. download<T = any>(
  169. config: AxiosRequestConfig,
  170. title: string,
  171. options?: RequestOptions
  172. ): Promise<T> {
  173. let conf: CreateAxiosOptions = cloneDeep({
  174. ...config,
  175. method: 'GET',
  176. responseType: 'blob'
  177. })
  178. const transform = this.getTransform()
  179. const { requestOptions } = this.options
  180. const opt: RequestOptions = Object.assign({}, requestOptions, options)
  181. const { beforeRequestHook, requestCatchHook } = transform || {}
  182. if (beforeRequestHook && isFunction(beforeRequestHook)) {
  183. conf = beforeRequestHook(conf, opt)
  184. }
  185. conf.requestOptions = opt
  186. conf = this.supportFormData(conf)
  187. return new Promise((resolve, reject) => {
  188. this.axiosInstance
  189. .request<any, AxiosResponse<RequestResult>>(conf)
  190. .then((res: AxiosResponse<RequestResult>) => {
  191. resolve(res as unknown as Promise<T>)
  192. // download file
  193. if (typeof res != undefined) {
  194. downloadByData(res?.data as unknown as BlobPart, title)
  195. }
  196. })
  197. .catch((e: Error | AxiosError) => {
  198. if (requestCatchHook && isFunction(requestCatchHook)) {
  199. reject(requestCatchHook(e, opt))
  200. return
  201. }
  202. if (axios.isAxiosError(e)) {
  203. // rewrite error message from axios in here
  204. }
  205. reject(e)
  206. })
  207. })
  208. }
  209. request<T = any>(config: AxiosRequestConfig, options?: RequestOptions): Promise<T> {
  210. let conf: CreateAxiosOptions = cloneDeep(config)
  211. const transform = this.getTransform()
  212. const { requestOptions } = this.options
  213. const opt: RequestOptions = Object.assign({}, requestOptions, options)
  214. const { beforeRequestHook, requestCatchHook, transformRequestHook } = transform || {}
  215. if (beforeRequestHook && isFunction(beforeRequestHook)) {
  216. conf = beforeRequestHook(conf, opt)
  217. }
  218. conf.requestOptions = opt
  219. conf = this.supportFormData(conf)
  220. return new Promise((resolve, reject) => {
  221. this.axiosInstance
  222. .request<any, AxiosResponse<RequestResult>>(conf)
  223. .then((res: AxiosResponse<RequestResult>) => {
  224. if (transformRequestHook && isFunction(transformRequestHook)) {
  225. try {
  226. const ret = transformRequestHook(res, opt)
  227. resolve(ret)
  228. } catch (err) {
  229. reject(err || new Error('request error!'))
  230. }
  231. return
  232. }
  233. resolve(res as unknown as Promise<T>)
  234. })
  235. .catch((e: Error | AxiosError) => {
  236. if (requestCatchHook && isFunction(requestCatchHook)) {
  237. reject(requestCatchHook(e, opt))
  238. return
  239. }
  240. if (axios.isAxiosError(e)) {
  241. // rewrite error message from axios in here
  242. }
  243. reject(e)
  244. })
  245. })
  246. }
  247. }