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

axios拦截器处理401重发请求后无法返回响应问题求助

解决axios拦截器处理401重发请求后无法返回响应的问题

问题根源梳理

  1. 响应拦截器未返回重发请求的Promise:原代码中401分支的重发请求逻辑仅在refreshToken().then回调内执行,没有将重发请求的Promise返回给拦截器的错误处理函数,导致原请求的Promise链无法获取重发后的响应结果。
  2. 拦截器重复注册:每次调用useJWT钩子都会重新添加axios拦截器,引发重复执行处理逻辑的问题。
  3. 请求配置复用错误:手动拆分原请求URL重发,未保留原请求的method、headers等完整配置,可能导致请求路径或参数错误。
  4. 全局headers污染:sendPostRequest直接修改axios全局默认headers,会影响其他无关请求。
  5. 变量提升问题:拦截器初始化时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");
  }
}, []);

关键修改说明

  1. 拦截器注册优化:将拦截器注册放到useEffect中,通过eject方法在组件卸载时清理,避免重复添加。
  2. Promise链修复:在401分支返回重发请求的Promise,让原请求的.then能获取到重发后的响应。
  3. 防止无限循环:添加_retry标记,避免refresh token失效时反复触发401拦截。
  4. 请求配置复用:直接复用原请求的config,更新token后重发,保留原请求的所有参数和方法。
  5. headers隔离:每个请求单独配置headers,不修改全局默认值,避免污染其他请求。

内容的提问来源于stack exchange,提问作者A.R.SEIF

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 04:30:30