index.ts 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  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 { log } from "console";
  17. import { message } from "../message"; */
  18. // 相关配置请参考:www.axios-js.com/zh-cn/docs/#axios-request-config-1
  19. const defaultConfig: AxiosRequestConfig = {
  20. // 请求超时时间
  21. timeout: 10000,
  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. // 优先判断post/get等方法是否传入回调,否则执行初始化设置等回调
  61. if (typeof config.beforeRequestCallback === "function") {
  62. config.beforeRequestCallback(config);
  63. return config;
  64. }
  65. if (PureHttp.initConfig.beforeRequestCallback) {
  66. PureHttp.initConfig.beforeRequestCallback(config);
  67. return config;
  68. }
  69. /** 请求白名单,放置一些不需要token的接口(通过设置请求白名单,防止token过期后再请求造成的死循环问题) */
  70. const whiteList = ["/refresh-token", "/login"];
  71. return whiteList.find(url => url === config.url)
  72. ? config
  73. : new Promise(resolve => {
  74. const data = getToken();
  75. if (data) {
  76. const now = new Date().getTime();
  77. const expired = parseInt(data.expires) - now <= 0;
  78. if (expired) {
  79. if (!PureHttp.isRefreshing) {
  80. PureHttp.isRefreshing = true;
  81. // token过期刷新
  82. useUserStoreHook()
  83. .handRefreshToken({ refreshToken: data.refreshToken })
  84. .then(res => {
  85. const token = res.data.accessToken;
  86. config.headers["Authorization"] = formatToken(token);
  87. PureHttp.requests.forEach(cb => cb(token));
  88. PureHttp.requests = [];
  89. })
  90. .finally(() => {
  91. PureHttp.isRefreshing = false;
  92. });
  93. }
  94. resolve(PureHttp.retryOriginalRequest(config));
  95. } else {
  96. config.headers["Authorization"] = formatToken(
  97. data.accessToken
  98. );
  99. resolve(config);
  100. }
  101. } else {
  102. resolve(config);
  103. }
  104. });
  105. },
  106. error => {
  107. return Promise.reject(error);
  108. }
  109. );
  110. }
  111. /** 响应拦截 */
  112. private httpInterceptorsResponse(): void {
  113. const instance = PureHttp.axiosInstance;
  114. instance.interceptors.response.use(
  115. (response: PureHttpResponse) => {
  116. const $config = response.config;
  117. // 关闭进度条动画
  118. NProgress.done();
  119. // 优先判断post/get等方法是否传入回调,否则执行初始化设置等回调
  120. if (typeof $config.beforeResponseCallback === "function") {
  121. $config.beforeResponseCallback(response);
  122. return response.data;
  123. }
  124. if (PureHttp.initConfig.beforeResponseCallback) {
  125. PureHttp.initConfig.beforeResponseCallback(response);
  126. return response.data;
  127. }
  128. return response.data;
  129. },
  130. (error: PureHttpError) => {
  131. const $error = error;
  132. $error.isCancelRequest = Axios.isCancel($error);
  133. // 关闭进度条动画
  134. NProgress.done();
  135. // 所有的响应异常 区分来源为取消请求/非取消请求
  136. return Promise.reject($error);
  137. }
  138. );
  139. }
  140. /** 通用请求工具函数 */
  141. public request<T>(
  142. method: RequestMethods,
  143. url: string,
  144. param?: AxiosRequestConfig,
  145. axiosConfig?: PureHttpRequestConfig
  146. ): Promise<T> {
  147. const config = {
  148. method,
  149. url,
  150. ...param,
  151. ...axiosConfig
  152. } as PureHttpRequestConfig;
  153. // 单独处理自定义请求/响应回调
  154. return new Promise((resolve, reject) => {
  155. PureHttp.axiosInstance
  156. .request(config)
  157. .then((response: undefined) => {
  158. resolve(response);
  159. })
  160. .catch(error => {
  161. reject(error);
  162. });
  163. });
  164. }
  165. /** 单独抽离的post工具函数 */
  166. public post<T, P>(
  167. url: string,
  168. params?: AxiosRequestConfig<T>,
  169. config?: PureHttpRequestConfig
  170. ): Promise<P> {
  171. return this.request<P>("post", url, params, config);
  172. }
  173. /** 单独抽离的get工具函数 */
  174. public get<T, P>(
  175. url: string,
  176. params?: AxiosRequestConfig<T>,
  177. config?: PureHttpRequestConfig
  178. ): Promise<P> {
  179. return this.request<P>("get", url, params, config);
  180. }
  181. }
  182. export const http = new PureHttp();