request.ts 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. /* global
  2. IRequestSuite
  3. */
  4. import type { AxiosInstance } from 'axios'
  5. import axios from 'axios'
  6. import Cookie from 'js-cookie'
  7. import Router from '@/router'
  8. import { currentHost } from '@/utils/location'
  9. // redirect error
  10. function errorRedirect(url: string) {
  11. Router.push(`/${ url }`)
  12. }
  13. // code Message
  14. const codeMessage: {
  15. [key: number]: string
  16. } = {
  17. 200: '服务器成功返回请求的数据。',
  18. 201: '新建或修改数据成功。',
  19. 202: '一个请求已经进入后台排队(异步任务)。',
  20. 204: '删除数据成功。',
  21. 206: '进行范围请求成功。',
  22. 400: '发出的请求有错误,服务器没有进行新建或修改数据的操作。',
  23. 401: '用户没有权限(令牌、用户名、密码错误)。',
  24. 403: '用户得到授权,但是访问是被禁止的。',
  25. 404: '发出的请求针对的是不存在的记录,服务器没有进行操作。',
  26. 405: '请求不允许。',
  27. 406: '请求的格式不可得。',
  28. 410: '请求的资源被永久删除,且不会再得到的。',
  29. 422: '当创建一个对象时,发生一个验证错误。',
  30. 500: '服务器发生错误,请检查服务器。',
  31. 502: '网关错误。',
  32. 503: '服务不可用,服务器暂时过载或维护。',
  33. 504: '网关超时。'
  34. }
  35. // 创建axios实例
  36. const service: AxiosInstance = axios.create({
  37. // api 的 base_url
  38. baseURL: currentHost.baseApi,
  39. // 请求超时时间
  40. timeout: 200000
  41. })
  42. // request拦截器
  43. service.interceptors.request.use(
  44. request => {
  45. const token: string | undefined = Cookie.get('token')
  46. // Conversion of hump nomenclature
  47. /**
  48. * 让每个请求携带自定义 token
  49. * 请根据实际情况自行修改
  50. */
  51. if (request.url === '/login') {
  52. return request
  53. }
  54. request.headers['Content-Type'] = 'multipart/form-data'
  55. request.headers!.Authorization = token as string
  56. return request
  57. },
  58. error => {
  59. return Promise.reject(error)
  60. }
  61. )
  62. // respone拦截器
  63. service.interceptors.response.use(
  64. response => {
  65. /**
  66. * response data
  67. * {
  68. * data: {},
  69. * msg: "",
  70. * error: 0 0 success | 1 error | 5000 failed | HTTP code
  71. * }
  72. */
  73. const data: any = response.data
  74. const msg: string = data.msg || ''
  75. if (msg.indexOf('user not log in') !== -1 && data.error === -1) {
  76. // TODO 写死的 之后要根据语言跳转
  77. errorRedirect('login')
  78. return
  79. }
  80. if (response.config.autoDownLoadFile === undefined || response.config.autoDownLoadFile) {
  81. Promise.resolve().then(() => {
  82. useResHeadersAPI(response.headers, data)
  83. })
  84. }
  85. if (
  86. response.request.responseType === 'blob' &&
  87. /json$/gi.test(response.headers['content-type'])
  88. ) {
  89. return new Promise(resolve => {
  90. const reader = new FileReader()
  91. reader.readAsText(<Blob>response.data)
  92. reader.onload = () => {
  93. if (!reader.result || typeof reader.result !== 'string') return resolve(response.data)
  94. response.data = JSON.parse(reader.result)
  95. resolve(response.data)
  96. }
  97. })
  98. } else if (data instanceof Blob) {
  99. return {
  100. data,
  101. msg: '',
  102. error: 0
  103. }
  104. }
  105. if (data.code && data.data) {
  106. return {
  107. data: data.data,
  108. error: data.code === 200 ? 0 : -1,
  109. msg: 'ok'
  110. }
  111. }
  112. if (!data.data && !data.msg && !data.error) {
  113. return {
  114. data,
  115. error: 0,
  116. msg: 'ok'
  117. }
  118. }
  119. if (data.msg === null) {
  120. data.msg = 'Unknown error'
  121. }
  122. return data
  123. },
  124. error => {
  125. /**
  126. * 某些特定的接口 404 500 需要跳转
  127. * 在需要重定向的接口中传入 redirect字段 值为要跳转的路由
  128. * redirect之后 调用接口的地方会继续执行
  129. * 因为此时 response error
  130. * 所以需要前端返回一个前端构造好的数据结构 避免前端业务部分逻辑出错
  131. * 不重定向的接口则不需要传
  132. */
  133. if (error.config.redirect) {
  134. errorRedirect(error.config.redirect)
  135. }
  136. if (error.response) {
  137. return {
  138. data: {},
  139. error: error.response.status,
  140. msg: codeMessage[error.response.status] || error.response.data.message
  141. }
  142. } else {
  143. // 某些特定的接口 failed 需要跳转
  144. console.log(error)
  145. return {
  146. data: {},
  147. error: 5000,
  148. aborted: error.config.signal?.aborted,
  149. msg: '服务请求不可用,请重试或检查您的网络。'
  150. }
  151. }
  152. }
  153. )
  154. export function sleep(time = 0) {
  155. return new Promise((resolve) => {
  156. setTimeout(() => {
  157. resolve({})
  158. }, time)
  159. })
  160. }
  161. function extractFileNameFromContentDispositionHeader(value: string) {
  162. const patterns = [
  163. /filename\*=[^']+'\w*'"([^"]+)";?/i,
  164. /filename\*=[^']+'\w*'([^;]+);?/i,
  165. /filename="([^;]*);?"/i,
  166. /filename=([^;]*);?/i
  167. ]
  168. let responseFilename: any = null
  169. patterns.some(regex => {
  170. responseFilename = regex.exec(value)
  171. return responseFilename !== null
  172. })
  173. if (responseFilename !== null && responseFilename.length > 1) {
  174. try {
  175. return decodeURIComponent(responseFilename[1])
  176. } catch (e) {
  177. console.error(e)
  178. }
  179. }
  180. return null
  181. }
  182. export function downloadFile(boldData: BlobPart, filename = '预设文件名称', type: any) {
  183. const blob = boldData instanceof Blob
  184. ? boldData
  185. : new Blob([boldData], {
  186. type
  187. })
  188. const url = window.URL.createObjectURL(blob)
  189. const link = document.createElement('a')
  190. link.style.display = 'none'
  191. link.href = url
  192. link.download = filename
  193. document.body.appendChild(link)
  194. link.click()
  195. document.body.removeChild(link)
  196. }
  197. export function useResHeadersAPI(headers: any, resData: any) {
  198. const disposition = headers['content-disposition']
  199. if (disposition) {
  200. let filename: string | null = ''
  201. filename = extractFileNameFromContentDispositionHeader(disposition)
  202. if (filename) {
  203. downloadFile(resData, filename, headers['content-type'])
  204. }
  205. }
  206. }
  207. const requestSuite: IRequestSuite = {
  208. get(uri, params, config) {
  209. return service.get(uri, {
  210. params,
  211. ...config
  212. })
  213. },
  214. post(uri, data, config) {
  215. return service.post(uri, data, config)
  216. },
  217. put(uri, data, config) {
  218. return service.put(uri, data, config)
  219. },
  220. patch(uri, data, config) {
  221. return service.patch(uri, data, config)
  222. },
  223. delete(uri, config) {
  224. return service.delete(uri, config)
  225. }
  226. }
  227. export default requestSuite