React+Spring Boot项目中Axios偶发401错误原因排查
可能的原因及修复方案
1. 异步token设置与组件渲染的竞态问题
你的AuthProvider在渲染时会异步调用SignInPostService设置Axios全局token,但这个异步操作完成前(isLoading仍为true),代码没有明确返回内容,导致React可能提前渲染子组件。此时子组件发起的API请求会因token未设置返回401,刷新页面后SignInPostService已完成,token正常设置,请求恢复正常。
修复方案:
修改AuthProvider逻辑,在isLoading为true时返回加载状态阻止子组件提前渲染,同时用useEffect处理异步逻辑(避免在渲染阶段执行副作用):
function AuthProvider({ children }: { children: React.ReactNode }) { const [user, setUser] = useState<any>(window.localStorage.getItem('userFromStorage')); const [isLoading, setIsLoading] = useState(true); let signin = (newUserMail: string, callback: VoidFunction, newUserObject: Object) => { return fakeAuthProvider.signin(() => { setUser(JSON.stringify(newUserMail)); window.localStorage.setItem('userFromStorage', JSON.stringify(newUserMail)); callback(); }); }; let signout = (callback: VoidFunction) => { return fakeAuthProvider.signout(() => { setUser(null); window.localStorage.removeItem('userFromStorage'); callback(); }); }; const value = { user, signin, signout }; // 用useEffect处理异步初始化逻辑 useEffect(() => { const initAuth = async () => { if (user) { try { await SignInPostService(JSON.parse(user).token); } catch (err) { // 处理初始化失败情况 window.localStorage.removeItem('userFromStorage'); setUser(null); } finally { setIsLoading(false); } } else { setIsLoading(false); } }; initAuth(); }, [user]); // 加载中时返回加载组件,避免子组件提前发起请求 if (isLoading) { return <div>Loading...</div>; // 替换为你的加载组件 } return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>; }
2. Axios全局头依赖不可靠 + 请求未等待token设置
页面刷新时Axios默认头会被重置,若某些请求在SignInPostService完成前触发,全局X-AUTH-TOKEN尚未设置,就会返回401。
修复方案:
放弃依赖Axios全局头,改为创建专用Axios实例并添加请求拦截器,动态从本地存储读取token:
// 创建专属Axios实例 const apiClient = axios.create({ baseURL: process.env.REACT_APP_API_SERVER_URL }); // 添加请求拦截器,自动注入token apiClient.interceptors.request.use(config => { const authToken = localStorage.getItem('authToken'); if (authToken) { config.headers['X-AUTH-TOKEN'] = authToken; } return config; }); // 用该实例发起请求 export function SignInGetService() { apiClient.get('/url') .then(response => { }); } export async function SignInPostService(token) { return apiClient.post('/mySignInUrl', { "token": token }) .then(response => { localStorage.setItem('authToken', response.data['data']); // 存储后端返回的token return response.data['data'] }).catch(function(error) { // 新增401状态处理 if(error.response.status === 500 || error.response.status === 401){ window.localStorage.removeItem('userFromStorage'); window.localStorage.removeItem('authToken'); window.location.href = '/SignIn'; } }); }
3. Google登录token过期未处理
你的SignInPostService仅处理500错误,若Google登录token过期,后端可能返回401,此时不会触发错误处理,导致Axios头未设置,后续请求返回401。刷新页面后Google可能自动刷新token,SignInPostService恢复正常。
修复方案:
在SignInPostService的catch中补充401状态处理:
export async function SignInPostService(token) { return await axios.post(process.env.REACT_APP_API_SERVER_URL + '/mySignInUrl', { "token": token }) .then(response => { axios.defaults.headers.common['X-AUTH-TOKEN'] = response.data['data']; return response.data['data'] }).catch(function(error) { if(error.response.status === 500 || error.response.status === 401){ window.localStorage.removeItem('userFromStorage'); window.location.href = '/SignIn'; } }); }
内容的提问来源于stack exchange,提问作者Ismael Hadj
相关产品推荐
相关产品推荐

