index.ts 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  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 { ElNotification } from 'element-plus'
  17. console.log('BASE URL', window.serviceCfg.baseURL)
  18. // 相关配置请参考:www.axios-js.com/zh-cn/docs/#axios-request-config-1
  19. const defaultConfig: AxiosRequestConfig = {
  20. // 请求超时时间
  21. timeout: 10000,
  22. baseURL: window.serviceCfg.baseURL,
  23. withCredentials: false,
  24. headers: {
  25. Accept: "application/json, text/plain, */*",
  26. "Content-Type": "application/json",
  27. "X-Requested-With": "XMLHttpRequest"
  28. },
  29. // 数组格式参数序列化(https://github.com/axios/axios/issues/5142)
  30. paramsSerializer: {
  31. serialize: stringify as unknown as CustomParamsSerializer
  32. }
  33. };
  34. class PureHttp {
  35. constructor() {
  36. this.httpInterceptorsRequest();
  37. this.httpInterceptorsResponse();
  38. }
  39. /** `token`过期后,暂存待执行的请求 */
  40. private static requests = [];
  41. /** 防止重复刷新`token` */
  42. private static isRefreshing = false;
  43. /** 初始化配置对象 */
  44. private static initConfig: PureHttpRequestConfig = {};
  45. /** 保存当前`Axios`实例对象 */
  46. private static axiosInstance: AxiosInstance = Axios.create(defaultConfig);
  47. /** 重连原始请求 */
  48. private static retryOriginalRequest(config: PureHttpRequestConfig) {
  49. return new Promise(resolve => {
  50. PureHttp.requests.push((token: string) => {
  51. config.headers["Authorization"] = formatToken(token);
  52. resolve(config);
  53. });
  54. });
  55. }
  56. /** 请求拦截 */
  57. private httpInterceptorsRequest(): void {
  58. PureHttp.axiosInstance.interceptors.request.use(
  59. async (config: PureHttpRequestConfig): Promise<any> => {
  60. // 开启进度条动画
  61. // NProgress.start();
  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 = ["/api/login/refreshToken", "/api/login/login"];
  73. return whiteList.some(url => config.url.endsWith(url))
  74. ? config
  75. : new Promise(resolve => {
  76. const data = getToken();
  77. if (data) {
  78. if (config.params) {
  79. config.params['token'] = data.token
  80. }
  81. if (config.data) {
  82. config.data['token'] = data.token
  83. }
  84. const now = new Date().getTime();
  85. const expired = parseInt(data.expires) - now <= 0;
  86. if (expired) {
  87. console.log('token expired');
  88. if (!PureHttp.isRefreshing) {
  89. PureHttp.isRefreshing = true;
  90. // token过期刷新
  91. useUserStoreHook()
  92. .handRefreshToken({ token: data.token })
  93. .then(res => {
  94. console.log('refresh token', res)
  95. const token = res.user.token;
  96. config.headers["Authorization"] = formatToken(token);
  97. PureHttp.requests.forEach(cb => cb(token));
  98. PureHttp.requests = [];
  99. })
  100. .finally(() => {
  101. PureHttp.isRefreshing = false;
  102. });
  103. }
  104. resolve(PureHttp.retryOriginalRequest(config));
  105. } else {
  106. config.headers["Authorization"] = formatToken(
  107. data.token
  108. );
  109. resolve(config);
  110. }
  111. } else {
  112. resolve(config);
  113. }
  114. });
  115. },
  116. error => {
  117. return Promise.reject(error);
  118. }
  119. );
  120. }
  121. /** 响应拦截 */
  122. private httpInterceptorsResponse(): void {
  123. const instance = PureHttp.axiosInstance;
  124. instance.interceptors.response.use(
  125. (response: PureHttpResponse) => {
  126. const $config = response.config;
  127. // 关闭进度条动画
  128. NProgress.done();
  129. if (response.data && response.data.tokenError) {
  130. ElNotification.error({
  131. title: 'Token失效',
  132. message: '将跳转登入界面,请重新登入',
  133. })
  134. useUserStoreHook().logOut();
  135. return Promise.reject(response.data);
  136. }
  137. // 优先判断post/get等方法是否传入回调,否则执行初始化设置等回调
  138. if (typeof $config.beforeResponseCallback === "function") {
  139. $config.beforeResponseCallback(response);
  140. return response.data;
  141. }
  142. if (PureHttp.initConfig.beforeResponseCallback) {
  143. PureHttp.initConfig.beforeResponseCallback(response);
  144. return response.data;
  145. }
  146. return response.data;
  147. },
  148. (error: PureHttpError) => {
  149. const $error = error;
  150. $error.isCancelRequest = Axios.isCancel($error);
  151. // 关闭进度条动画
  152. NProgress.done();
  153. ElNotification.error({
  154. title: error.code,
  155. message: error.message,
  156. })
  157. console.log('error', error)
  158. // 所有的响应异常 区分来源为取消请求/非取消请求
  159. return Promise.reject($error);
  160. }
  161. );
  162. }
  163. /** 通用请求工具函数 */
  164. public request<T>(
  165. method: RequestMethods,
  166. url: string,
  167. param?: AxiosRequestConfig,
  168. axiosConfig?: PureHttpRequestConfig
  169. ): Promise<T> {
  170. const config = {
  171. method,
  172. url,
  173. ...param,
  174. ...axiosConfig
  175. } as PureHttpRequestConfig;
  176. // 单独处理自定义请求/响应回调
  177. return new Promise((resolve, reject) => {
  178. PureHttp.axiosInstance
  179. .request(config)
  180. .then((response: undefined) => {
  181. resolve(response);
  182. })
  183. .catch(error => {
  184. reject(error);
  185. });
  186. });
  187. }
  188. /** 单独抽离的`post`工具函数 */
  189. public post<T, P>(
  190. url: string,
  191. params?: AxiosRequestConfig<P>,
  192. config?: PureHttpRequestConfig
  193. ): Promise<T> {
  194. return this.request<T>("post", url, params, config);
  195. }
  196. /** 单独抽离的`get`工具函数 */
  197. public get<T, P>(
  198. url: string,
  199. params?: AxiosRequestConfig<P>,
  200. config?: PureHttpRequestConfig
  201. ): Promise<T> {
  202. return this.request<T>("get", url, params, config);
  203. }
  204. }
  205. export const http = new PureHttp();