You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 07:34:18