request.ts 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. /**
  2. * 该文件可自行根据业务逻辑进行调整
  3. */
  4. import type { AxiosResponseHeaders, RequestClientOptions } from '@vben/request';
  5. import { useAppConfig } from '@vben/hooks';
  6. import { preferences } from '@vben/preferences';
  7. import {
  8. authenticateResponseInterceptor,
  9. defaultResponseInterceptor,
  10. errorMessageResponseInterceptor,
  11. RequestClient,
  12. } from '@vben/request';
  13. import { useAccessStore } from '@vben/stores';
  14. import { message } from 'ant-design-vue';
  15. import JSONBigInt from 'json-bigint';
  16. import { useAuthStore } from '#/store';
  17. import { refreshTokenApi } from './core';
  18. const { apiURL } = useAppConfig(import.meta.env, import.meta.env.PROD);
  19. function createRequestClient(baseURL: string, options?: RequestClientOptions) {
  20. const client = new RequestClient({
  21. ...options,
  22. baseURL,
  23. transformResponse: (data: any, header: AxiosResponseHeaders) => {
  24. // storeAsString指示将BigInt存储为字符串,设为false则会存储为内置的BigInt类型
  25. return header.getContentType()?.toString().includes('application/json')
  26. ? JSONBigInt({ storeAsString: true }).parse(data)
  27. : data;
  28. },
  29. });
  30. /**
  31. * 重新认证逻辑
  32. */
  33. async function doReAuthenticate() {
  34. console.warn('Access token or refresh token is invalid or expired. ');
  35. const accessStore = useAccessStore();
  36. const authStore = useAuthStore();
  37. accessStore.setAccessToken(null);
  38. if (
  39. preferences.app.loginExpiredMode === 'modal' &&
  40. accessStore.isAccessChecked
  41. ) {
  42. accessStore.setLoginExpired(true);
  43. } else {
  44. await authStore.logout();
  45. }
  46. }
  47. /**
  48. * 刷新token逻辑
  49. */
  50. async function doRefreshToken() {
  51. const accessStore = useAccessStore();
  52. const resp = await refreshTokenApi();
  53. const newToken = resp.data;
  54. accessStore.setAccessToken(newToken);
  55. return newToken;
  56. }
  57. function formatToken(token: null | string) {
  58. return token ? `Bearer ${token}` : null;
  59. }
  60. // 请求头处理
  61. client.addRequestInterceptor({
  62. fulfilled: async (config) => {
  63. const accessStore = useAccessStore();
  64. config.headers.Authorization = formatToken(accessStore.accessToken);
  65. config.headers['Accept-Language'] = preferences.app.locale;
  66. return config;
  67. },
  68. });
  69. // 处理返回的响应数据格式
  70. client.addResponseInterceptor(
  71. defaultResponseInterceptor({
  72. codeField: 'code',
  73. dataField: 'data',
  74. successCode: 0,
  75. }),
  76. );
  77. // token过期的处理
  78. client.addResponseInterceptor(
  79. authenticateResponseInterceptor({
  80. client,
  81. doReAuthenticate,
  82. doRefreshToken,
  83. enableRefreshToken: preferences.app.enableRefreshToken,
  84. formatToken,
  85. }),
  86. );
  87. // 通用的错误处理,如果没有进入上面的错误处理逻辑,就会进入这里
  88. client.addResponseInterceptor(
  89. errorMessageResponseInterceptor((msg: string, error) => {
  90. // 这里可以根据业务进行定制,你可以拿到 error 内的信息进行定制化处理,根据不同的 code 做不同的提示,而不是直接使用 message.error 提示 msg
  91. // 当前mock接口返回的错误字段是 error 或者 message
  92. const responseData = error?.response?.data ?? {};
  93. const errorMessage = responseData?.error ?? responseData?.message ?? '';
  94. // 如果没有错误信息,则会根据状态码进行提示
  95. message.error(errorMessage || msg);
  96. }),
  97. );
  98. return client;
  99. }
  100. export const requestClient = createRequestClient(apiURL, {
  101. responseReturn: 'data',
  102. });
  103. export const baseRequestClient = new RequestClient({ baseURL: apiURL });
  104. export interface PageFetchParams {
  105. [key: string]: any;
  106. pageNo?: number;
  107. pageSize?: number;
  108. }