Axios拦截器重试401请求时修改POST请求Content-Type问题
问题分析与解决方案
核心问题:请求头被完全覆盖
你的请求拦截器中,这行代码直接替换了整个请求头对象:
config.headers = { Authorization: `Bearer ${authStore.user.accessToken}`, };
原请求的Content-Type等头信息会被彻底清空,重试时自然无法保留正确的Content-Type。
修复步骤
1. 保留原有请求头,仅追加Authorization
修改请求拦截器的头信息设置逻辑,改为扩展而非替换headers:
axios.interceptors.request.use( (config) => { const authStore = useAuthStore(); if (!authStore.loggedIn) { authStore.setUserFromStorage(); if (!authStore.loggedIn) { return config; } } if (config?.headers && authStore.user.accessToken) { // 不要直接替换headers,而是追加Authorization字段 config.headers.Authorization = `Bearer ${authStore.user.accessToken}`; // 如果担心headers不存在,也可以用Object.assign兜底 // config.headers = Object.assign({}, config.headers, { // Authorization: `Bearer ${authStore.user.accessToken}` // }); } return config; }, (error) => Promise.reject(error) );
2. 确保重试时请求体的序列化状态
Axios在首次请求时会自动序列化JSON请求体并设置Content-Type: application/json,但err.config中的data可能已经是序列化后的字符串。重试时需要确认config.headers中存在正确的Content-Type,如果没有可以手动补充:
在重试请求前添加:
// 重试前检查并补全Content-Type if (!err.config.headers['Content-Type']) { err.config.headers['Content-Type'] = 'application/json'; } resolve(axios(err.config));
优化你的令牌刷新实现
改用实例拦截器而非全局拦截器
你创建了自定义的api实例,但拦截器挂载在了全局axios上,这会导致全局所有axios请求都受影响。应该改为给你的api实例添加拦截器:
// 在创建api实例的文件中添加拦截器 api.interceptors.request.use(/* ... */); api.interceptors.response.use(/* ... */);
用Promise队列替代定时器轮询
轮询的方式不够优雅,可以用一个全局Promise队列来等待令牌刷新完成:
// 在authStore中添加一个存储等待队列的变量 // 比如:pendingRequests: Array<(token: string) => void> = []; // 修改响应拦截器的等待逻辑 } else { // 非首个请求,加入等待队列 return new Promise((resolve) => { authStore.pendingRequests.push((token) => { err.config.headers.Authorization = `Bearer ${token}`; resolve(api(err.config)); }); }); } // 令牌刷新成功后,遍历队列执行所有等待的请求 authStore.setUserInfo(res.data as User); authStore.pendingRequests.forEach(callback => callback(res.data.accessToken)); authStore.pendingRequests = [];
这样可以避免定时器的性能损耗,逻辑更清晰。
内容的提问来源于stack exchange,提问作者OverMars
相关产品推荐
相关产品推荐

