axios拦截器处理401重发请求后无法返回响应问题求助
解决axios拦截器处理401重发请求后无法返回响应的问题
问题根源梳理
- 响应拦截器未返回重发请求的Promise:原代码中401分支的重发请求逻辑仅在
refreshToken().then回调内执行,没有将重发请求的Promise返回给拦截器的错误处理函数,导致原请求的Promise链无法获取重发后的响应结果。 - 拦截器重复注册:每次调用
useJWT钩子都会重新添加axios拦截器,引发重复执行处理逻辑的问题。 - 请求配置复用错误:手动拆分原请求URL重发,未保留原请求的method、headers等完整配置,可能导致请求路径或参数错误。
- 全局headers污染:
sendPostRequest直接修改axios全局默认headers,会影响其他无关请求。 - 变量提升问题:拦截器初始化时
refreshToken和sendPostRequest尚未定义,存在调用风险。
完整解决方案
步骤1:修改useJWT钩子,修复拦截器逻辑
import React, { useEffect } from "react"; import axios from "axios"; const API_URL = "http://127.0.0.1:4000/api/"; function useJWT() { const refreshToken = () => { const token = localStorage.getItem("refresh"); return axios .post(API_URL + "token", { token }) .then((response) => { if (response.data.access) localStorage.setItem("access", response.data.access); if (response.data.refresh) localStorage.setItem("refresh", response.data.refresh); return response.data; }); }; function login(email, password) { return axios .post(API_URL + "login", { email, password }) .then((response) => { if (response.data.access) localStorage.setItem("access", response.data.access); if (response.data.refresh) localStorage.setItem("refresh", response.data.refresh); return response.data; }); } const sendPostRequest = (url, data) => { console.log(300); const token = localStorage.getItem("access"); // 单独配置当前请求的headers,不修改全局默认值 return axios.post(API_URL + url, { data }, { headers: { "jwt": token, "Accept": "application/json" } }); }; const logout = () => { const token = localStorage.getItem("refresh"); return axios .delete(API_URL + "logout", { headers: { "jwt": localStorage.getItem("access") }, data: { token } // Delete请求参数需放在data中 }) .then(() => { localStorage.removeItem("access"); localStorage.removeItem("refresh"); }); }; // 用useEffect注册拦截器,避免重复注册 useEffect(() => { // 请求拦截器 const requestInterceptor = axios.interceptors.request.use( (request) => { if (!request.headers.Accept) request.headers["Accept"] = "application/json"; console.log("request Send "); return request; }, (error) => Promise.reject(error) ); // 响应拦截器 const responseInterceptor = axios.interceptors.response.use( (response) => { console.log("answer = ", response); return response; }, async (error) => { const originalRequest = error.response.config; // 添加_retry标记,防止401错误无限循环 if (error.response.status === 401 && !originalRequest._retry) { originalRequest._retry = true; try { await refreshToken(); // 更新原请求的token originalRequest.headers["jwt"] = localStorage.getItem("access"); // 重发原请求,返回Promise给原请求链 return axios(originalRequest); } catch (refreshError) { // Refresh token失效,执行登出逻辑 logout(); return Promise.reject(refreshError); } } return Promise.reject(error); } ); // 组件卸载时清理拦截器 return () => { axios.interceptors.request.eject(requestInterceptor); axios.interceptors.response.eject(responseInterceptor); }; }, []); return { login, logout, refreshToken, sendPostRequest }; } export default useJWT;
步骤2:App.js代码保持不变(确保正确引入钩子)
// 导入useJWT和路由导航方法 const { sendPostRequest } = useJWT(); const getUser = () => { console.log(12); return sendPostRequest("user"); }; useEffect(() => { let token = localStorage.getItem("access"); console.log("token = ", token); if (token != null) { // 模拟401错误场景 localStorage.setItem("access", ""); getUser() .then((response) => console.log("response 1= ", response)) .catch((exception) => console.log("exception = ", exception.toString())) .then((response) => console.log("response 2= ", response)); } else { navigate("/login"); } }, []);
关键修改说明
- 拦截器注册优化:将拦截器注册放到
useEffect中,通过eject方法在组件卸载时清理,避免重复添加。 - Promise链修复:在401分支返回重发请求的Promise,让原请求的
.then能获取到重发后的响应。 - 防止无限循环:添加
_retry标记,避免refresh token失效时反复触发401拦截。 - 请求配置复用:直接复用原请求的
config,更新token后重发,保留原请求的所有参数和方法。 - headers隔离:每个请求单独配置headers,不修改全局默认值,避免污染其他请求。
内容的提问来源于stack exchange,提问作者A.R.SEIF
相关产品推荐
相关产品推荐

