React项目中如何为Apisauce创建拦截器并实现登录页跳转?
在React+Apisauce+React Router+Redux Saga中实现拦截器跳转登录页
当然可以实现!不过Apisauce的monitor本身是纯函数,没法直接访问React Router的路由跳转能力,得稍微做些处理才行。结合你的技术栈,这里给你两种可行的方案:
方案一:给Apisauce传递路由实例,在monitor中直接跳转
核心思路是把React Router的history(v5)或navigate(v6)实例传递给你的API类,让tokenMonitor能调用它来完成跳转。
步骤1:获取路由实例
- React Router v5:用
withRouter高阶组件包裹你的组件,组件props里会自带history对象。 - React Router v6:通过Context传递
useNavigate钩子返回的跳转函数(v6不再支持withRouter给类组件传history)。
先写个v6的Context示例:
// NavigateContext.tsx import { createContext, useContext, useNavigate, ReactNode } from 'react-router-dom'; const NavigateContext = createContext<((to: string) => void) | null>(null); export const NavigateProvider = ({ children }: { children: ReactNode }) => { const navigate = useNavigate(); return ( <NavigateContext.Provider value={navigate}> {children} </NavigateContext.Provider> ); }; export const useNavigateContext = () => useContext(NavigateContext);
记得在根组件里用NavigateProvider包裹所有路由内的组件。
步骤2:修改API类,接收路由实例
class ApiService { private api: ApisauceInstance; private navigate: (to: string) => void; // 构造函数接收navigate/history实例 constructor(navigate: (to: string) => void) { this.navigate = navigate; this.api = apisauce.create({ baseURL: env.API_URL, headers: { Authorization: `Bearer ${localStorage.getItem('user') || ''}`, }, timeout: 15000, }); // 绑定this上下文,确保monitor里能访问到this.navigate this.tokenMonitor = this.tokenMonitor.bind(this); this.api.addMonitor(this.tokenMonitor); } private tokenMonitor(response: any) { const { ok, status } = response; // 建议精准判断401状态(token过期/未授权),而非所有非200的情况 if (!ok && status === 401) { localStorage.removeItem('user'); // 调用路由跳转 this.navigate('/login'); } } }
步骤3:在组件中注入路由实例
import { useNavigateContext } from './NavigateContext'; function App() { const navigate = useNavigateContext(); // 初始化API服务,传入navigate const apiService = useMemo(() => new ApiService(navigate!), [navigate]); // ... 其他组件逻辑 }
方案二:在Redux Saga中处理错误并跳转
既然你用了Redux Saga管理异步请求,把跳转逻辑放在Saga里会更符合Redux的数据流,也能让API层更纯粹。
示例代码:
import { call, put, takeLatest } from 'redux-saga/effects'; import { push } from 'connected-react-router'; // 用connected-react-router同步路由和Redux import { api } from './apiService'; import { FETCH_DATA, fetchDataFailure } from './actions'; function* fetchDataSaga(action: any) { try { const response = yield call(api.fetchData, action.payload); // 处理请求成功的逻辑 } catch (error: any) { // 捕获401状态 if (error.response?.status === 401) { yield call(localStorage.removeItem, 'user'); // 触发路由跳转 yield put(push('/login')); } // 派发错误action yield put(fetchDataFailure(error)); } } export function* watchFetchData() { yield takeLatest(FETCH_DATA, fetchDataSaga); }
这种方案的好处是,所有异步请求的错误处理都集中在Saga里,逻辑更清晰,也不需要给API层传递路由依赖。
小提示
- 建议优先判断
401状态码,而非所有非200的响应——毕竟像400(参数错误)这类情况不需要跳登录页。 - 如果用方案一,要注意避免重复跳转(比如多个请求同时返回401),可以加个状态锁来控制。
内容的提问来源于stack exchange,提问作者Konstantin Kudelko
相关产品推荐
相关产品推荐

