刷新Token后请求结果无法返回至Pinia Store的问题求助
问题描述
通过Pinia Store使用Axios发起定时请求,存在多请求并发场景。服务端采用需刷新的Token机制,已编写拦截器在Access Token过期前调用Refresh Token,并将Token刷新期间的请求存入数组。目前Token刷新功能正常,但刷新后的请求结果无法返回至发起请求的Pinia Store,导致数据未更新。
用户提供的代码:
import axios from 'axios'; import {useAuthStore} from "@/stores/Authentication/AppAuthStore.js"; import VueJwtDecode from 'vue-jwt-decode'; axios.defaults.baseURL = import.meta.env.VITE_API_ENDPOINT; axios.defaults.maxRedirects = 0; let refreshing_token = null; let myTimeout= null; let busy = false; let requestArray = []; const baseURL = import.meta.env.VITE_API_ENDPOINT; axios.interceptors.request.use( async req =>{ if (req.url.endsWith("refresh")) { // if we are making a refresh token call, return it to prevent infinite loop return req; } const accesstoken = sessionStorage.getItem("accesstoken") if (accesstoken){ req.headers.Authorization = `Bearer ${accesstoken}`; //const {header,payload} = useJwt(accesstoken) let user = VueJwtDecode.decode(accesstoken); const isExpired = Date.now() >= ((user.exp * 1000) - 10000) console.log(isExpired) if (!isExpired) return req; requestArray.push(req); console.log("is expired"); /* refreshing_token = refreshing_token ? refreshing_token : refresh_token(); let res = await refreshing_token; refreshing_token = null; clearTimeout(myTimeout);*/ if (!busy){ busy = true; const refreshtoken = sessionStorage.getItem("refreshtoken") axios.post(baseURL + 'auth/refresh', { refresh_token : refreshtoken }).then((res) => { console.log(res); console.log("token refreshed : " + res.data.accesstoken); const authappstore = useAuthStore() authappstore.isAppOK = true; sessionStorage.setItem("accesstoken",res.data.accesstoken); sessionStorage.setItem("refreshtoken", res.data.refreshtoken); req.headers.Authorization = `Bearer ${res.data.accesstoken}`; if (requestArray.length !== 0) { requestArray.forEach(x => { try { console.log(x); axios(x) } catch (e) { console.log(e) } }); requestArray = []; } }) .catch((err) => { const errStatus = err.statusCode || 500; const errMsg = err.message || 'Something went wrong'; console.log(errMsg) }) .finally(() => { busy = false; }); } } else { return req } }) export default axios;
解决方案
核心问题分析
- 原拦截器在Token过期后,仅将请求推入数组,未返回等待Token刷新完成后再执行的Promise,导致原请求(来自Pinia)无法获取后续响应。
- 重新发起请求时,直接调用
axios(x)但未将结果关联到原请求的Promise链,Pinia发起的请求一直处于pending状态。 - 全局刷新状态管理不严谨,可能导致重复触发刷新请求。
修正后的代码
import axios from 'axios'; import { useAuthStore } from "@/stores/Authentication/AppAuthStore.js"; import VueJwtDecode from 'vue-jwt-decode'; axios.defaults.baseURL = import.meta.env.VITE_API_ENDPOINT; axios.defaults.maxRedirects = 0; // 用全局Promise存储正在进行的刷新请求,避免并发重复刷新 let refreshPromise = null; const baseURL = import.meta.env.VITE_API_ENDPOINT; axios.interceptors.request.use(async req => { if (req.url.endsWith("refresh")) { return req; } const accesstoken = sessionStorage.getItem("accesstoken"); if (!accesstoken) { return req; } req.headers.Authorization = `Bearer ${accesstoken}`; const user = VueJwtDecode.decode(accesstoken); const isExpired = Date.now() >= ((user.exp * 1000) - 10000); if (!isExpired) { return req; } // 如果正在刷新Token,等待刷新完成 if (refreshPromise) { await refreshPromise; // 刷新完成后更新当前请求的Token req.headers.Authorization = `Bearer ${sessionStorage.getItem("accesstoken")}`; return req; } // 开始刷新Token refreshPromise = new Promise(async (resolve, reject) => { try { const refreshtoken = sessionStorage.getItem("refreshtoken"); const res = await axios.post(`${baseURL}auth/refresh`, { refresh_token: refreshtoken }); console.log("token refreshed : " + res.data.accesstoken); const authappstore = useAuthStore(); authappstore.isAppOK = true; sessionStorage.setItem("accesstoken", res.data.accesstoken); sessionStorage.setItem("refreshtoken", res.data.refreshtoken); resolve(res.data.accesstoken); } catch (err) { const errStatus = err.response?.status || 500; const errMsg = err.response?.data?.message || 'Something went wrong'; console.log(errMsg); reject(err); } finally { // 刷新完成后重置刷新Promise refreshPromise = null; } }); // 等待刷新完成,更新当前请求的Token后返回 await refreshPromise; req.headers.Authorization = `Bearer ${sessionStorage.getItem("accesstoken")}`; return req; }); export default axios;
关键改动说明
- 用全局
refreshPromise管理刷新状态:避免多个并发请求重复触发Token刷新,所有过期请求都会等待同一个刷新Promise完成。 - 将原请求的执行逻辑绑定到刷新Promise:Token过期时,原请求会等待刷新完成,更新请求头后继续执行,确保响应能正常返回给Pinia。
- 去掉冗余的
requestArray:通过Promise链自动处理所有等待的请求,无需手动存储和遍历请求配置。 - 完善错误处理:捕获刷新请求的错误并传递给原请求,避免请求静默失败。
内容的提问来源于stack exchange,提问作者John Lauwers
相关产品推荐
相关产品推荐

