request.ts 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. import axios, { AxiosInstance, AxiosRequestConfig } from 'axios';
  2. import { ElMessage } from 'element-plus';
  3. import { Local, Session } from '/@/utils/storage';
  4. // 定义请求中止控制器映射表
  5. const abortControllerMap: Map<string, AbortController> = new Map();
  6. // 配置新建一个 axios 实例
  7. export const service = axios.create({
  8. baseURL: window.__env__.VITE_API_URL as any,
  9. timeout: 50000,
  10. headers: { 'Content-Type': 'application/json' },
  11. });
  12. // token 键定义
  13. export const accessTokenKey = 'access-token';
  14. export const refreshAccessTokenKey = `x-${accessTokenKey}`;
  15. // 获取 token
  16. export const getToken = () => {
  17. return Local.get(accessTokenKey);
  18. };
  19. // 清除 token
  20. export const clearAccessTokens = () => {
  21. Local.remove(accessTokenKey);
  22. Local.remove(refreshAccessTokenKey);
  23. // 清除其他
  24. Session.clear();
  25. // 刷新浏览器
  26. window.location.reload();
  27. };
  28. // axios 默认实例
  29. export const axiosInstance: AxiosInstance = axios;
  30. // 添加请求拦截器
  31. service.interceptors.request.use(
  32. (config) => {
  33. // // 在发送请求之前做些什么 token
  34. // if (Session.get('token')) {
  35. // (<any>config.headers).common['Authorization'] = `${Session.get('token')}`;
  36. // }
  37. // 记录中止控制信息
  38. const controller = new AbortController();
  39. config.signal = controller.signal;
  40. const url = config.url || '';
  41. abortControllerMap.set(url, controller);
  42. // 获取本地的 token
  43. const accessToken = Local.get(accessTokenKey);
  44. if (accessToken) {
  45. // 将 token 添加到请求报文头中
  46. config.headers!['Authorization'] = `Bearer ${accessToken}`;
  47. // 判断 accessToken 是否过期
  48. const jwt: any = decryptJWT(accessToken);
  49. const exp = getJWTDate(jwt.exp as number);
  50. // token 已经过期
  51. if (new Date() >= exp) {
  52. // 获取刷新 token
  53. const refreshAccessToken = Local.get(refreshAccessTokenKey);
  54. // 携带刷新 token
  55. if (refreshAccessToken) {
  56. config.headers!['X-Authorization'] = `Bearer ${refreshAccessToken}`;
  57. }
  58. }
  59. // debugger
  60. // get请求映射params参数
  61. if (config.method?.toLowerCase() === 'get' && config.data) {
  62. let url = config.url + '?' + tansParams(config.data);
  63. url = url.slice(0, -1);
  64. config.data = {};
  65. config.url = url;
  66. }
  67. }
  68. return config;
  69. },
  70. (error) => {
  71. // 对请求错误做些什么
  72. return Promise.reject(error);
  73. }
  74. );
  75. // 添加响应拦截器
  76. service.interceptors.response.use(
  77. (res) => {
  78. // 请求结束后清除中止控制项
  79. const url = res.config.url || '';
  80. abortControllerMap.delete(url);
  81. // 获取状态码和返回数据
  82. var status = res.status;
  83. var serve = res.data;
  84. // 处理 401
  85. if (status === 401) {
  86. clearAccessTokens();
  87. }
  88. // 处理未进行规范化处理的
  89. if (status >= 400) {
  90. throw new Error(res.statusText || 'Request Error.');
  91. }
  92. // 处理规范化结果错误
  93. if (serve && serve.hasOwnProperty('errors') && serve.errors) {
  94. throw new Error(JSON.stringify(serve.errors || 'Request Error.'));
  95. }
  96. // 读取响应报文头 token 信息
  97. var accessToken = res.headers[accessTokenKey];
  98. var refreshAccessToken = res.headers[refreshAccessTokenKey];
  99. // 判断是否是无效 token
  100. if (accessToken === 'invalid_token') {
  101. clearAccessTokens();
  102. }
  103. // 判断是否存在刷新 token,如果存在则存储在本地
  104. else if (refreshAccessToken && accessToken && accessToken !== 'invalid_token') {
  105. Local.set(accessTokenKey, accessToken);
  106. Local.set(refreshAccessTokenKey, refreshAccessToken);
  107. }
  108. // 响应拦截及自定义处理
  109. if (serve.code === 401) {
  110. clearAccessTokens();
  111. } else if (serve.code === undefined) {
  112. return Promise.resolve(res);
  113. } else if (serve.code !== 200) {
  114. var message;
  115. // 判断 serve.message 是否为对象
  116. if (serve.message && typeof serve.message == 'object') {
  117. message = JSON.stringify(serve.message);
  118. } else {
  119. message = serve.message;
  120. }
  121. ElMessage({
  122. dangerouslyUseHTMLString: true,
  123. message: message,
  124. type: 'error',
  125. });
  126. throw new Error(message);
  127. }
  128. return res;
  129. },
  130. (error) => {
  131. // 处理响应错误
  132. if (error.response) {
  133. if (error.response.status === 401) {
  134. clearAccessTokens();
  135. }
  136. }
  137. // 对响应错误做点什么
  138. if (error.message.indexOf('timeout') != -1) {
  139. ElMessage.error('网络超时');
  140. } else if (error.message == 'Network Error') {
  141. ElMessage.error('网络连接错误');
  142. } else {
  143. if (error.response.data) ElMessage.error(error.response.statusText);
  144. else ElMessage.error('接口路径找不到');
  145. }
  146. return Promise.reject(error);
  147. }
  148. );
  149. // 取消指定请求
  150. export const cancelRequest = (url: string | string[]) => {
  151. const urlList = Array.isArray(url) ? url : [url];
  152. for (const _url of urlList) {
  153. abortControllerMap.get(_url)?.abort();
  154. abortControllerMap.delete(_url);
  155. }
  156. }
  157. // 取消全部请求
  158. export const cancelAllRequest = () => {
  159. for (const [_, controller] of abortControllerMap) {
  160. controller.abort();
  161. }
  162. abortControllerMap.clear();
  163. }
  164. /**
  165. * 参数处理
  166. * @param {*} params 参数
  167. */
  168. export function tansParams(params: any) {
  169. let result = '';
  170. for (const propName of Object.keys(params)) {
  171. const value = params[propName];
  172. var part = encodeURIComponent(propName) + '=';
  173. if (value !== null && value !== '' && typeof value !== 'undefined') {
  174. if (typeof value === 'object') {
  175. for (const key of Object.keys(value)) {
  176. if (value[key] !== null && value[key] !== '' && typeof value[key] !== 'undefined') {
  177. let params = propName + '[' + key + ']';
  178. var subPart = encodeURIComponent(params) + '=';
  179. result += subPart + encodeURIComponent(value[key]) + '&';
  180. }
  181. }
  182. } else {
  183. result += part + encodeURIComponent(value) + '&';
  184. }
  185. }
  186. }
  187. return result;
  188. }
  189. /**
  190. * 解密 JWT token 的信息
  191. * @param token jwt token 字符串
  192. * @returns <any>object
  193. */
  194. export function decryptJWT(token: string): any {
  195. token = token.replace(/_/g, '/').replace(/-/g, '+');
  196. var json = decodeURIComponent(escape(window.atob(token.split('.')[1])));
  197. return JSON.parse(json);
  198. }
  199. /**
  200. * 将 JWT 时间戳转换成 Date
  201. * @description 主要针对 `exp`,`iat`,`nbf`
  202. * @param timestamp 时间戳
  203. * @returns Date 对象
  204. */
  205. export function getJWTDate(timestamp: number): Date {
  206. return new Date(timestamp * 1000);
  207. }
  208. /**
  209. * Ajax请求,如果成功返回result字段,如果不成功提示错误信息
  210. * @description Ajax请求
  211. * @config AxiosRequestConfig 请求参数
  212. * @returns 返回对象
  213. */
  214. export function request2(config: AxiosRequestConfig<any>): any {
  215. return new Promise((resolve, reject) => {
  216. service(config)
  217. .then((res) => {
  218. if (res.data.type == 'success') {
  219. resolve(res.data.result);
  220. } else {
  221. console.log('res', res);
  222. ElMessage.success(res.data.message);
  223. }
  224. })
  225. .catch((res) => {
  226. console.log('res', res);
  227. ElMessage.error(res);
  228. reject(res);
  229. });
  230. });
  231. }
  232. // 导出 axios 实例
  233. export default service;