import axios, { type AxiosInstance, type InternalAxiosRequestConfig, } from "axios"; import { ENV } from "../config"; import { getAccessToken, getRefreshToken, setTokens, clearTokens, } from "@/utils/token"; import { Message } from "@arco-design/web-vue"; // 是否正在刷新 token let isRefreshing = false; // 存储因为 token 过期而挂起的请求 let requests: ((token: string) => void)[] = []; const request: AxiosInstance = axios.create({ baseURL: ENV.API_BASE_URL, timeout: 10000, withCredentials: false, }); // ======================= // 请求拦截器 // ======================= request.interceptors.request.use( (config: InternalAxiosRequestConfig) => { // 自动携带 access token const token = getAccessToken(); if (token) { config.headers = config.headers || {}; config.headers.Authorization = `Bearer ${token}`; } return config; }, (error) => { return error; } ); // ======================= // 响应拦截器 // ======================= request.interceptors.response.use( (response) => { // console.log("响应: ", response); const data = response.data; /**TODO: 增加响应码处理 */ return data; }, async (error) => { const originalRequest = error.config; // 处理 401 未授权情况 (Token 过期) // 确保不是刷新 token 的请求本身 (避免死循环) if ( error.response?.status === 401 && !originalRequest._retry && !originalRequest.url.includes("/auth/refresh") ) { // 如果正在刷新,将当前请求加入队列等待 if (isRefreshing) { return new Promise((resolve) => { requests.push((token: string) => { originalRequest.headers.Authorization = `Bearer ${token}`; resolve(request(originalRequest)); }); }); } originalRequest._retry = true; isRefreshing = true; try { const refreshToken = getRefreshToken(); if (!refreshToken) { throw new Error("No refresh token available"); } // 使用原生 axios 发送刷新请求,避免拦截器循环 const response = await axios.post( `${ENV.API_BASE_URL}/v1/auth/refresh`, null, { params: { refreshToken }, } ); if (response.data?.success) { const { accessToken, refreshToken: newRefreshToken } = response.data.data; // 更新本地存储 setTokens(accessToken, newRefreshToken); // 执行队列中的请求 requests.forEach((cb) => cb(accessToken)); requests = []; // 重试当前请求 originalRequest.headers.Authorization = `Bearer ${accessToken}`; return request(originalRequest); } else { throw new Error("Refresh token failed"); } } catch (refreshError) { console.error("Token 刷新失败:", refreshError); // 清除过期 token clearTokens(); Message.error("登录已过期,请重新登录"); // 这里可以选择跳转到登录页,例如 window.location.href = '/user/login' // 建议让路由守卫或者页面自行处理未登录状态 return Promise.reject(refreshError); } finally { isRefreshing = false; } } /** * Promise.reject(error) 用来 返回一个状态为 rejected 的 Promise,相当于“主动抛出错误”, * * 让调用者能够在 .catch() 或 try/catch 中捕获这个错误。 * 它在 异步流程、拦截器、错误处理 中非常常见。 */ console.error("❌ 网络错误:", error); return Promise.reject(error); } ); export default request;