axios-utils.ts 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. /**
  2. * 当前版本:v1.0.6
  3. * 使用描述:https://editor.swagger.io 代码生成 typescript-axios 辅组工具库
  4. * 依赖说明:适配 axios 版本:v0.21.4
  5. * 视频教程:https://www.bilibili.com/video/BV1EW4y1C71D
  6. */
  7. import globalAxios, { AxiosInstance } from 'axios';
  8. import { Configuration } from '../api-services';
  9. import { BaseAPI, BASE_PATH } from '../api-services/base';
  10. import { ElMessage } from 'element-plus';
  11. import { Local, Session } from '../utils/storage';
  12. // 接口服务器配置
  13. export const serveConfig = new Configuration({
  14. basePath: import.meta.env.VITE_API_URL,
  15. });
  16. // token 键定义
  17. export const accessTokenKey = 'access-token';
  18. export const refreshAccessTokenKey = `x-${accessTokenKey}`;
  19. // 获取 token
  20. export const getToken = () => {
  21. return Local.get(accessTokenKey);
  22. };
  23. // 清除 token
  24. export const clearAccessTokens = () => {
  25. clearTokens();
  26. // 刷新浏览器
  27. window.location.reload();
  28. };
  29. // 清除 token
  30. export const clearTokens = () => {
  31. Local.remove(accessTokenKey);
  32. Local.remove(refreshAccessTokenKey);
  33. Session.clear();
  34. };
  35. // axios 默认实例
  36. export const axiosInstance: AxiosInstance = globalAxios;
  37. // 这里可以配置 axios 更多选项 =========================================
  38. // axios 请求拦截
  39. axiosInstance.interceptors.request.use(
  40. (conf) => {
  41. // 获取本地的 token
  42. const accessToken = Local.get(accessTokenKey);
  43. if (accessToken) {
  44. // 将 token 添加到请求报文头中
  45. conf.headers!['Authorization'] = `Bearer ${accessToken}`;
  46. // 判断 accessToken 是否过期
  47. const jwt: any = decryptJWT(accessToken);
  48. const exp = getJWTDate(jwt.exp as number);
  49. // token 已经过期
  50. if (new Date() >= exp) {
  51. // 获取刷新 token
  52. const refreshAccessToken = Local.get(refreshAccessTokenKey);
  53. // 携带刷新 token
  54. if (refreshAccessToken) {
  55. conf.headers!['X-Authorization'] = `Bearer ${refreshAccessToken}`;
  56. }
  57. }
  58. }
  59. // 这里编写请求拦截代码 =========================================
  60. return conf;
  61. },
  62. (error) => {
  63. // 处理请求错误
  64. if (error.request) {
  65. ElMessage.error(error);
  66. }
  67. // 请求错误代码及自定义处理
  68. ElMessage.error(error);
  69. return Promise.reject(error);
  70. }
  71. );
  72. // axios 响应拦截
  73. axiosInstance.interceptors.response.use(
  74. (res) => {
  75. // 获取状态码和返回数据
  76. var status = res.status;
  77. var serve = res.data;
  78. // 处理 401
  79. if (status === 401) {
  80. clearAccessTokens();
  81. }
  82. // 处理未进行规范化处理的
  83. if (status >= 400) {
  84. throw new Error(res.statusText || 'Request Error.');
  85. }
  86. // 处理规范化结果错误
  87. if (serve && serve.hasOwnProperty('errors') && serve.errors) {
  88. throw new Error(JSON.stringify(serve.errors || 'Request Error.'));
  89. }
  90. // 读取响应报文头 token 信息
  91. var accessToken = res.headers[accessTokenKey];
  92. var refreshAccessToken = res.headers[refreshAccessTokenKey];
  93. // 判断是否是无效 token
  94. if (accessToken === 'invalid_token') {
  95. clearAccessTokens();
  96. }
  97. // 判断是否存在刷新 token,如果存在则存储在本地
  98. else if (refreshAccessToken && accessToken && accessToken !== 'invalid_token') {
  99. Local.set(accessTokenKey, accessToken);
  100. Local.set(refreshAccessTokenKey, refreshAccessToken);
  101. }
  102. // 响应拦截及自定义处理
  103. if (serve.code === 401) {
  104. clearAccessTokens();
  105. } else if (serve.code === undefined) {
  106. return Promise.resolve(res);
  107. } else if (serve.code !== 200) {
  108. var message = JSON.stringify(serve.message);
  109. ElMessage.error(message);
  110. throw new Error(message);
  111. }
  112. return res;
  113. },
  114. (error) => {
  115. // 处理响应错误
  116. if (error.response) {
  117. if (error.response.status === 401) {
  118. clearAccessTokens();
  119. }
  120. }
  121. // 响应错误代码及自定义处理
  122. ElMessage.error(error);
  123. return Promise.reject(error);
  124. }
  125. );
  126. /**
  127. * 包装 Promise 并返回 [Error, any]
  128. * @param promise Promise 方法
  129. * @param errorExt 自定义错误信息(拓展)
  130. * @returns [Error, any]
  131. */
  132. export function feature<T, U = Error>(promise: Promise<T>, errorExt?: object): Promise<[U, undefined] | [null, T]> {
  133. return promise
  134. .then<[null, T]>((data: T) => [null, data])
  135. .catch<[U, undefined]>((err: U) => {
  136. if (errorExt) {
  137. const parsedError = Object.assign({}, err, errorExt);
  138. return [parsedError, undefined];
  139. }
  140. return [err, undefined];
  141. });
  142. }
  143. /**
  144. * 获取/创建服务 API 实例
  145. * @param apiType BaseAPI 派生类型
  146. * @param configuration 服务器配置对象
  147. * @param basePath 服务器地址
  148. * @param axiosObject axios 实例
  149. * @returns 服务API 实例
  150. */
  151. export function getAPI<T extends BaseAPI>(
  152. // eslint-disable-next-line no-unused-vars
  153. apiType: new (configuration?: Configuration, basePath?: string, axiosInstance?: AxiosInstance) => T,
  154. configuration: Configuration = serveConfig,
  155. basePath: string = BASE_PATH,
  156. axiosObject: AxiosInstance = axiosInstance
  157. ) {
  158. return new apiType(configuration, basePath, axiosObject);
  159. }
  160. /**
  161. * 解密 JWT token 的信息
  162. * @param token jwt token 字符串
  163. * @returns <any>object
  164. */
  165. export function decryptJWT(token: string): any {
  166. token = token.replace(/_/g, '/').replace(/-/g, '+');
  167. var json = decodeURIComponent(escape(window.atob(token.split('.')[1])));
  168. return JSON.parse(json);
  169. }
  170. /**
  171. * 将 JWT 时间戳转换成 Date
  172. * @description 主要针对 `exp`,`iat`,`nbf`
  173. * @param timestamp 时间戳
  174. * @returns Date 对象
  175. */
  176. export function getJWTDate(timestamp: number): Date {
  177. return new Date(timestamp * 1000);
  178. }