index.ts 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. import Axios, {
  2. type AxiosInstance,
  3. type AxiosRequestConfig,
  4. type CustomParamsSerializer
  5. } from "axios";
  6. import type {
  7. PureHttpError,
  8. RequestMethods,
  9. PureHttpResponse,
  10. PureHttpRequestConfig
  11. } from "./types.d";
  12. import { stringify } from "qs";
  13. import NProgress from "../progress";
  14. import { getToken, formatToken } from "@/utils/auth";
  15. import { useUserStoreHook } from "@/store/modules/user";
  16. import { message } from "@/utils/message";
  17. // 相关配置请参考:www.axios-js.com/zh-cn/docs/#axios-request-config-1
  18. const defaultConfig: AxiosRequestConfig = {
  19. // 请求超时时间
  20. timeout: 10000,
  21. baseURL: import.meta.env.VITE_BASE_URL,
  22. headers: {
  23. Accept: "application/json, text/plain, */*",
  24. "Content-Type": "application/json",
  25. "X-Requested-With": "XMLHttpRequest"
  26. },
  27. // 数组格式参数序列化(https://github.com/axios/axios/issues/5142)
  28. paramsSerializer: {
  29. serialize: stringify as unknown as CustomParamsSerializer
  30. }
  31. };
  32. class PureHttp {
  33. constructor() {
  34. this.httpInterceptorsRequest();
  35. this.httpInterceptorsResponse();
  36. }
  37. /** token过期后,暂存待执行的请求 */
  38. private static requests = [];
  39. /** 防止重复刷新token */
  40. private static isRefreshing = false;
  41. /** 初始化配置对象 */
  42. private static initConfig: PureHttpRequestConfig = {};
  43. /** 保存当前Axios实例对象 */
  44. private static axiosInstance: AxiosInstance = Axios.create(defaultConfig);
  45. /** 重连原始请求 */
  46. private static retryOriginalRequest(config: PureHttpRequestConfig) {
  47. return new Promise(resolve => {
  48. PureHttp.requests.push((token: string) => {
  49. config.headers["Authorization"] = formatToken(token);
  50. resolve(config);
  51. });
  52. });
  53. }
  54. /** 请求拦截 */
  55. private httpInterceptorsRequest(): void {
  56. PureHttp.axiosInstance.interceptors.request.use(
  57. async (config: PureHttpRequestConfig): Promise<any> => {
  58. // 开启进度条动画
  59. NProgress.start();
  60. const token = localStorage.getItem("token") || "";
  61. config.headers["satoken"] = token;
  62. // 优先判断post/get等方法是否传入回调,否则执行初始化设置等回调
  63. if (typeof config.beforeRequestCallback === "function") {
  64. config.beforeRequestCallback(config);
  65. return config;
  66. }
  67. if (PureHttp.initConfig.beforeRequestCallback) {
  68. PureHttp.initConfig.beforeRequestCallback(config);
  69. return config;
  70. }
  71. /** 请求白名单,放置一些不需要token的接口(通过设置请求白名单,防止token过期后再请求造成的死循环问题) */
  72. const whiteList = ["/refresh-token", "/login"];
  73. return whiteList.find(url => url === config.url)
  74. ? config
  75. : new Promise(resolve => {
  76. const data = getToken();
  77. if (data) {
  78. const now = new Date().getTime();
  79. const expired = parseInt(data.expires) - now <= 0;
  80. if (expired) {
  81. if (!PureHttp.isRefreshing) {
  82. PureHttp.isRefreshing = true;
  83. // token过期刷新
  84. useUserStoreHook()
  85. .handRefreshToken({ refreshToken: data.refreshToken })
  86. .then(res => {
  87. const token = res.data.accessToken;
  88. config.headers["Authorization"] = formatToken(token);
  89. PureHttp.requests.forEach(cb => cb(token));
  90. PureHttp.requests = [];
  91. })
  92. .finally(() => {
  93. PureHttp.isRefreshing = false;
  94. });
  95. }
  96. resolve(PureHttp.retryOriginalRequest(config));
  97. } else {
  98. config.headers["Authorization"] = formatToken(
  99. data.accessToken
  100. );
  101. resolve(config);
  102. }
  103. } else {
  104. resolve(config);
  105. }
  106. });
  107. },
  108. error => {
  109. return Promise.reject(error);
  110. }
  111. );
  112. }
  113. /** 响应拦截 */
  114. private httpInterceptorsResponse(): void {
  115. const instance = PureHttp.axiosInstance;
  116. instance.interceptors.response.use(
  117. (response: PureHttpResponse) => {
  118. const $config = response.config;
  119. // 关闭进度条动画
  120. NProgress.done();
  121. // 统一返回接口报错信息
  122. if (response.data.code && response.data.code !== 200) {
  123. if ($config.url && $config.url.includes('/specialPortrait/getPersonDimensionChartsRanking') && response.data.code === 10002) {
  124. console.log('不报错')
  125. } else {
  126. message(response.data.msg, { type: "error" });
  127. }
  128. }
  129. if (typeof $config.beforeResponseCallback === "function") {
  130. // 优先判断post/get等方法是否传入回调,否则执行初始化设置等回调
  131. $config.beforeResponseCallback(response);
  132. return response.data;
  133. }
  134. if (PureHttp.initConfig.beforeResponseCallback) {
  135. PureHttp.initConfig.beforeResponseCallback(response);
  136. return response.data;
  137. }
  138. return response.data;
  139. },
  140. (error: PureHttpError) => {
  141. const $error = error;
  142. $error.isCancelRequest = Axios.isCancel($error);
  143. // 关闭进度条动画
  144. NProgress.done();
  145. // console.log("响应拦截错误", $error);
  146. // @ts-expect-error
  147. message($error.response.data.msg || "请求失败", { type: "error" });
  148. // 所有的响应异常 区分来源为取消请求/非取消请求
  149. return Promise.reject($error);
  150. }
  151. );
  152. }
  153. /** 通用请求工具函数 */
  154. public request<T>(
  155. method: RequestMethods,
  156. url: string,
  157. param?: AxiosRequestConfig,
  158. axiosConfig?: PureHttpRequestConfig
  159. ): Promise<T> {
  160. const config = {
  161. method,
  162. url,
  163. ...param,
  164. ...axiosConfig
  165. } as PureHttpRequestConfig;
  166. // 单独处理自定义请求/响应回调
  167. return new Promise((resolve, reject) => {
  168. PureHttp.axiosInstance
  169. .request(config)
  170. .then((response: undefined) => {
  171. resolve(response);
  172. })
  173. .catch(error => {
  174. reject(error);
  175. });
  176. });
  177. }
  178. /** 单独抽离的post工具函数 */
  179. public post<T, P>(
  180. url: string,
  181. params?: AxiosRequestConfig<T>,
  182. config?: PureHttpRequestConfig
  183. ): Promise<P> {
  184. return this.request<P>("post", url, params, config);
  185. }
  186. /** 单独抽离的get工具函数 */
  187. public get<T, P>(
  188. url: string,
  189. params?: AxiosRequestConfig<T>,
  190. config?: PureHttpRequestConfig
  191. ): Promise<P> {
  192. return this.request<P>("get", url, params, config);
  193. }
  194. }
  195. export const http = new PureHttp();