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

React中如何基于Hook/Context配置AccessToken的Axios中间件

统一处理Axios请求头的可维护方案

核心问题

假设访问令牌通过Hook暴露,如何以可维护的方式配置Hook,统一处理Axios API请求中的令牌请求头?

当前已通过Context暴露访问令牌/权限并实现受保护路由,但希望在API调用环节利用该Hook设置Axios拦截器管理认证请求头,遇到的问题是API调用似乎需要嵌套在Hook中才能使用令牌,不清楚具体实现方式;同时使用react-query,期望全局配置但需在组件中访问Hook。


1. 独立封装Axios实例

不要直接修改全局axios.defaults,创建独立的Axios实例避免污染全局配置,提升可维护性:

// api/axiosInstance.ts
import axios from 'axios';

const apiInstance = axios.create({
  baseURL: process.env.REACT_APP_API_BASE_URL, // 替换为你的API基础地址
});

export default apiInstance;

2. 在AuthProvider中配置动态拦截器

利用AuthProvider的useEffect监听令牌变化,动态添加/更新请求拦截器,同时处理令牌失效的场景:

import React, { useContext, useState, useEffect, useMemo } from 'react';
import { useNavigate, useLocation } from 'react-router-dom';
import { useMutation } from '@tanstack/react-query';
import axios from 'axios';
import apiInstance from './api/axiosInstance'; // 导入封装的Axios实例

const AuthContext = React.createContext<any>(null);

export const useAuth = () => {
  return React.useContext(AuthContext);
};

const loginApiCall = (userName: string, password: string) => {
  if (!userName || !password) { return Promise.reject('缺少凭据') }
  return axios.post(`${auth_service}/oauth/token`, { username: userName, password: password })
};

const AuthProvider = ({ children }: any) => {
  const navigate = useNavigate();
  const [token, setToken] = useState<string | null>(null);
  const location = useLocation();

  // 监听令牌变化,动态配置Axios拦截器
  useEffect(() => {
    // 请求拦截器:自动添加Authorization头
    const requestInterceptor = apiInstance.interceptors.request.use(
      (config) => {
        if (token) {
          config.headers.Authorization = `Bearer ${token.access_token}`;
        }
        return config;
      },
      (error) => Promise.reject(error)
    );

    // 响应拦截器:处理401未授权,触发登出
    const responseInterceptor = apiInstance.interceptors.response.use(
      (response) => response,
      (error) => {
        if (error.response?.status === 401 && token) {
          handleLogout();
        }
        return Promise.reject(error);
      }
    );

    // 清理拦截器,避免内存泄漏
    return () => {
      apiInstance.interceptors.request.eject(requestInterceptor);
      apiInstance.interceptors.response.eject(responseInterceptor);
    };
  }, [token]);

  // 原有路由导航逻辑
  useEffect(() => {
    if (location.pathname === '/login' && token) {
      navigate('/');
    } else if (!token) {
      navigate('/login');
    }
  }, [token, location.pathname, navigate]);

  const loginCall = useMutation((data: any) => loginApiCall(data.username, data.password), {
    onSuccess: (response) => {
      setToken(response.data);
    }
  });

  const handleLogin = async (username: string, password: string) => {
    loginCall.mutate({ username, password });
  };

  const handleLogout = () => {
    setToken(null);
    // 可选:调用登出API使令牌失效
    // apiInstance.post('/logout').catch(() => {});
    navigate('/login');
  };

  const value = useMemo(() => ({
    token,
    onLogin: handleLogin,
    onLogout: handleLogout,
  }), [token]);

  return (
    <AuthContext.Provider value={value}>
      {children}
    </AuthContext.Provider>
  );
};

export default AuthProvider;

3. 在React Query中使用封装的Axios实例

所有API请求直接使用apiInstance,自动继承认证头,无需在每个query/mutation中手动配置:

// hooks/useUserProfile.ts
import { useQuery } from '@tanstack/react-query';
import apiInstance from '../api/axiosInstance';

export const useUserProfile = () => {
  return useQuery({
    queryKey: ['userProfile'],
    queryFn: () => apiInstance.get('/user/profile').then(res => res.data),
  });
};

方案优势

  • 集中管理:所有认证相关的Axios配置统一在AuthProvider中,便于后续修改维护
  • 自动同步:令牌更新/清除时,拦截器自动调整请求头,无需手动干预
  • 避免冲突:独立Axios实例不会影响登录等无需认证的请求
  • 减少重复代码:React Query的所有请求自动带上认证头,无需重复编写逻辑

内容的提问来源于stack exchange,提问作者iamaword

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 19:35:34