如何根据access_token过期状态与URL动态设置Axios请求头?
动态设置Axios请求头并处理多API的token过期问题
当然可以动态设置Axios请求头,针对你这种不同API对应不同认证token、需要在token过期时自动切换的场景,用Axios的请求拦截器和响应拦截器就能完美解决,下面直接给你改造方案:
第一步:重构token管理逻辑
首先你原来的代码里直接同步调用axios.get拿token是错误的,因为axios请求是异步的,得改成异步函数来获取并管理token的状态,包括token值和过期时间:
// config.js import axios from 'axios' // 存储两个API的token信息,包括token值和过期时间戳 let tokenCache = { api1: { token: '', expireTime: 0 }, api2: { token: '', expireTime: 0 } } // 用于避免并发请求重复刷新token的锁 let isRefreshing = { api1: false, api2: false } // 存储等待重试的请求队列 let requestQueue = { api1: [], api2: [] } // 获取api_1的token async function getApi1Token() { const response = await axios.get('url/access_token_1') const { access_token_1, expires_in } = response.data // 计算过期时间(当前时间 + 有效期,留10秒缓冲避免提前过期) tokenCache.api1.token = access_token_1 tokenCache.api1.expireTime = Date.now() + (expires_in - 10) * 1000 return access_token_1 } // 获取api_2的token async function getApi2Token() { const response = await axios.get('url/access_token_2') const { access_token_2, expires_in } = response.data tokenCache.api2.token = access_token_2 tokenCache.api2.expireTime = Date.now() + (expires_in - 10) * 1000 return access_token_2 } // 判断token是否过期 function isTokenExpired(apiType) { return Date.now() > tokenCache[apiType].expireTime || !tokenCache[apiType].token }
第二步:给Axios实例添加拦截器
接下来给你的Axios实例添加请求拦截器(请求前设置正确的token)和响应拦截器(处理token过期后的重试):
// 继续在config.js里写 export const instance = axios.create({ // 这里不需要默认设置Authorization,交给拦截器处理 }) // 请求拦截器:根据URL判断API类型,设置对应token instance.interceptors.request.use(async (config) => { let apiType // 根据请求URL判断属于哪个API if (config.url.includes('/sas/')) { apiType = 'api1' } else if (config.url.includes('/sps/')) { apiType = 'api2' } if (apiType) { // 如果token过期,先刷新token if (isTokenExpired(apiType)) { // 如果正在刷新当前API的token,把请求加入队列等待 if (isRefreshing[apiType]) { return new Promise((resolve) => { requestQueue[apiType].push(() => resolve(config)) }) } isRefreshing[apiType] = true try { // 重新获取token await (apiType === 'api1' ? getApi1Token() : getApi2Token()) // 刷新完成后,执行队列里的请求 requestQueue[apiType].forEach(cb => cb()) requestQueue[apiType] = [] } finally { isRefreshing[apiType] = false } } // 设置对应API的认证头 config.headers.Authorization = `Bearer ${tokenCache[apiType].token}` } return config }, (error) => { return Promise.reject(error) }) // 响应拦截器:处理接口返回的401(token过期或无效),刷新token后重试请求 instance.interceptors.response.use((response) => { return response }, async (error) => { const originalRequest = error.config // 判断是否是401错误,且不是已经重试过的请求 if (error.response?.status === 401 && !originalRequest._retry) { originalRequest._retry = true let apiType if (originalRequest.url.includes('/sas/')) { apiType = 'api1' } else if (originalRequest.url.includes('/sps/')) { apiType = 'api2' } if (apiType) { // 强制刷新token await (apiType === 'api1' ? getApi1Token() : getApi2Token()) // 设置新的token后重试请求 originalRequest.headers.Authorization = `Bearer ${tokenCache[apiType].token}` return instance(originalRequest) } } return Promise.reject(error) })
第三步:API调用代码无需修改
你原来的API调用代码可以完全保留,拦截器会自动在请求前处理token的过期判断和设置:
//Api_1 export const getCountry = async (country: string) => { const response = await instance.get( `/sas/${country}` ) return response.data } //Api_2 export const getCity = async (city: string) => { const response = await instance.get( `/sps/${city}` ) return response.data }
关键逻辑说明
- token过期判断:通过对比当前时间和预先计算的过期时间戳,提前10秒刷新token避免请求时刚好过期
- 并发请求处理:用锁变量和请求队列,避免多个请求同时触发token刷新,造成重复请求
- URL匹配:通过请求URL里的特征路径(
/sas/、/sps/)判断属于哪个API,设置对应token - 401重试:如果接口返回401,说明token确实失效了,强制刷新后重试原请求
内容的提问来源于stack exchange,提问作者Ae Leung
相关产品推荐
相关产品推荐

