preset-interceptors.ts 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. import type { RequestClient } from './request-client';
  2. import type { MakeErrorMessageFn, ResponseInterceptorConfig } from './types';
  3. import { $t } from '@vben/locales';
  4. import { isFunction } from '@vben/utils';
  5. import axios from 'axios';
  6. export const defaultResponseInterceptor = ({
  7. codeField = 'code',
  8. dataField = 'data',
  9. successCode = 0,
  10. }: {
  11. /** 响应数据中代表访问结果的字段名 */
  12. codeField: string;
  13. /** 响应数据中装载实际数据的字段名,或者提供一个函数从响应数据中解析需要返回的数据 */
  14. dataField: ((response: any) => any) | string;
  15. /** 当codeField所指定的字段值与successCode相同时,代表接口访问成功。如果提供一个函数,则返回true代表接口访问成功 */
  16. successCode: ((code: any) => boolean) | number | string;
  17. }): ResponseInterceptorConfig => {
  18. return {
  19. fulfilled: (response) => {
  20. const { config, data: responseData, status } = response;
  21. if (config.responseReturn === 'raw') {
  22. return response;
  23. }
  24. if (status >= 200 && status < 400) {
  25. if (config.responseReturn === 'body') {
  26. return responseData;
  27. } else if (
  28. isFunction(successCode)
  29. ? successCode(responseData[codeField])
  30. : responseData[codeField] === successCode
  31. ) {
  32. return isFunction(dataField)
  33. ? dataField(responseData)
  34. : responseData[dataField];
  35. }
  36. }
  37. throw Object.assign({}, response, { response });
  38. },
  39. };
  40. };
  41. export const authenticateResponseInterceptor = ({
  42. client,
  43. doReAuthenticate,
  44. doRefreshToken,
  45. enableRefreshToken,
  46. formatToken,
  47. }: {
  48. client: RequestClient;
  49. doReAuthenticate: () => Promise<void>;
  50. doRefreshToken: () => Promise<string>;
  51. enableRefreshToken: boolean;
  52. formatToken: (token: string) => null | string;
  53. }): ResponseInterceptorConfig => {
  54. return {
  55. rejected: async (error) => {
  56. const { config, response } = error;
  57. // 如果不是 401 错误,直接抛出异常
  58. if (response?.status !== 401) {
  59. throw error;
  60. }
  61. // 判断是否启用了 refreshToken 功能
  62. // 如果没有启用或者已经是重试请求了,直接跳转到重新登录
  63. if (!enableRefreshToken || config.__isRetryRequest) {
  64. await doReAuthenticate();
  65. throw error;
  66. }
  67. // 如果正在刷新 token,则将请求加入队列,等待刷新完成
  68. if (client.isRefreshing) {
  69. return new Promise((resolve) => {
  70. client.refreshTokenQueue.push((newToken: string) => {
  71. config.headers.Authorization = formatToken(newToken);
  72. resolve(client.request(config.url, { ...config }));
  73. });
  74. });
  75. }
  76. // 标记开始刷新 token
  77. client.isRefreshing = true;
  78. // 标记当前请求为重试请求,避免无限循环
  79. config.__isRetryRequest = true;
  80. try {
  81. const newToken = await doRefreshToken();
  82. // 处理队列中的请求
  83. client.refreshTokenQueue.forEach((callback) => callback(newToken));
  84. // 清空队列
  85. client.refreshTokenQueue = [];
  86. return client.request(error.config.url, { ...error.config });
  87. } catch (refreshError) {
  88. // 如果刷新 token 失败,处理错误(如强制登出或跳转登录页面)
  89. client.refreshTokenQueue.forEach((callback) => callback(''));
  90. client.refreshTokenQueue = [];
  91. console.error('Refresh token failed, please login again.');
  92. await doReAuthenticate();
  93. throw refreshError;
  94. } finally {
  95. client.isRefreshing = false;
  96. }
  97. },
  98. };
  99. };
  100. export const errorMessageResponseInterceptor = (
  101. makeErrorMessage?: MakeErrorMessageFn,
  102. ): ResponseInterceptorConfig => {
  103. return {
  104. rejected: (error: any) => {
  105. if (axios.isCancel(error)) {
  106. return Promise.reject(error);
  107. }
  108. const err: string = error?.toString?.() ?? '';
  109. let errMsg = '';
  110. if (err?.includes('Network Error')) {
  111. errMsg = $t('ui.fallback.http.networkError');
  112. } else if (error?.message?.includes?.('timeout')) {
  113. errMsg = $t('ui.fallback.http.requestTimeout');
  114. }
  115. if (errMsg) {
  116. makeErrorMessage?.(errMsg, error);
  117. return Promise.reject(error);
  118. }
  119. let errorMessage = '';
  120. const status = error?.response?.status;
  121. switch (status) {
  122. case 400: {
  123. errorMessage = $t('ui.fallback.http.badRequest');
  124. break;
  125. }
  126. case 401: {
  127. errorMessage = $t('ui.fallback.http.unauthorized');
  128. break;
  129. }
  130. case 403: {
  131. errorMessage = $t('ui.fallback.http.forbidden');
  132. break;
  133. }
  134. case 404: {
  135. errorMessage = $t('ui.fallback.http.notFound');
  136. break;
  137. }
  138. case 408: {
  139. errorMessage = $t('ui.fallback.http.requestTimeout');
  140. break;
  141. }
  142. default: {
  143. errorMessage = $t('ui.fallback.http.internalServerError');
  144. }
  145. }
  146. makeErrorMessage?.(errorMessage, error);
  147. return Promise.reject(error);
  148. },
  149. };
  150. };