Axios重试请求丢失Headers问题排查求助
基于Axios创建API请求实例,请求拦截器自动添加Bearer授权头;响应拦截器捕获401(Token过期)响应时,通过刷新机制获取新Token并重试原请求。但重试时发现Axios自动生成的Content-Type等Headers丢失,导致后端API无法正常识别请求。
注:未手动设置Content-Type,该头由Axios自动生成,困惑为何重试时这些头会丢失。
相关代码:
const api = axios.create({ baseURL: "https://www.mycoolapi.com", timeout: 5000, withCredentials: true, }); api.interceptors.request.use((config) => { // Add the auth header (not messing with any other headers) config.headers!.Authorization = `Bearer ${accessToken}`; return config; }, (error) => Promise.reject(error)); api.interceptors.response.use((response) => response, async (error) => { if (error.response.status == 401 && refreshToken && !error.config._isRetry) { // Token expired? Refresh, then retry the original request error.config._isRetry = true; await refreshLogIn(); return api(error.config); } throw error; }); async function refreshLogIn() { // Use the refresh token to get a new token pair const response = await api.post( "/token", new URLSearchParams({ grant_type: "refresh_token", refresh_token: refreshToken, }) ); accessToken = response.data["access_token"]; refreshToken = response.data["refresh_token"]; }
调试发现:响应拦截器中准备重试的error.config包含完整Headers,但进入请求拦截器重试时,Content-Type及Symbol(defaults)中的内容已丢失。
核心原因
直接复用error.config发起重试请求时,Axios不会重新触发默认的头信息处理逻辑——包括自动根据请求体生成Content-Type的逻辑。error.config里的Headers是第一次请求时的快照,但重试时原有的默认头关联(如Symbol(defaults))已失效,Axios也不会重新分析请求体来生成对应的Content-Type。
具体解决方案
方案1:重新构建请求配置
不要直接传递error.config,而是基于它创建新的配置对象,让Axios重新执行头信息处理流程:
// 替换响应拦截器中的return api(error.config); return api({ ...error.config, headers: { ...error.config.headers } // 浅拷贝原始头,避免引用冲突 });
这种方式会让Axios重新识别请求体类型,自动生成正确的Content-Type,同时保留原始请求的所有参数。
方案2:合并实例默认头(备选)
如果需要严格复用原有配置结构,可以将实例的默认头合并到error.config中:
error.config.headers = { ...api.defaults.headers.common, ...api.defaults.headers[error.config.method], ...error.config.headers }; return api(error.config);
注意:这种方式仅能恢复静态默认头,无法处理Axios根据请求体动态生成的Content-Type,因此方案1更可靠。
优化:隔离刷新Token的请求
当前refreshLogIn使用自定义的api实例发起请求,可能触发自身的请求拦截器(比如添加旧的过期Token),建议改用原生Axios发起刷新请求,避免拦截器干扰:
async function refreshLogIn() { const response = await axios.post( "https://www.mycoolapi.com/token", new URLSearchParams({ grant_type: "refresh_token", refresh_token: refreshToken, }), { withCredentials: true } ); accessToken = response.data["access_token"]; refreshToken = response.data["refresh_token"]; }
验证方式
重试时检查请求配置的headers,确认Content-Type与请求体匹配:
- JSON请求体对应
application/json - URLSearchParams对应
application/x-www-form-urlencoded
内容的提问来源于stack exchange,提问作者josh2112

