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

React 18中Axios响应拦截器失效,重新请求未带token求助

问题描述

React 18项目中使用Axios响应拦截器实现token刷新逻辑时出现异常:

  • 刷新token接口能正常返回新的idToken,且已更新存储,但重新发起请求时,请求头未携带Authorization字段
  • 打印error.response.config显示Authorization已正确设置,但实际请求仍缺失该字段,导致请求陷入无限循环
  • 相同代码在React 16项目中可正常运行

相关代码如下:

响应拦截器代码

axios.interceptors.response.use(
  (response) => {
    return response;
  },
  (error) => {
    if (error.response.status === 401) {
      let usersData = JSON.parse(localStorage.getItem("userData"));
      const refreshToken = usersData.refreshToken;
      return axios
        .post(
          `${api_base_url}/auth/authentication/refresh_token`,
          JSON.stringify({
            refresh_token: refreshToken,
          })
        )
        .then((response) => {
          usersData["accessToken"] = response.data.data.accessToken;
          usersData["idToken"] = response.data.data.idToken;

          setSessionStorage("userData", usersData);

          error.response.config.headers[
            "Authorization"
          ] = `Bearer ${response.data.data.idToken}`;

          return axios(error.response.config);
        })
        .catch((error) => {
          if (error.response.data.code !== "TOKEN_EXPIRED") {
            return;
          }
          localStorage.clear();
          window.location = "/login";
        });
    }
    return Promise.reject(error);
  }
);

请求封装代码

function getIRequestProp(severType, isMultipart, isSocial) {
    const serverUrl = severType ? social_api_base_url : api_base_url;
    let userData = JSON.parse(localStorage.getItem('userData'));
    let idToken;
    idToken = userData !== null ? userData['idToken'] : '';
    let content_type;
    if (isSocial) {
        content_type = 'application/x-www-form-urlencoded'
    } else {
        content_type = isMultipart ? 'multipart/form-data' : 'application/json'
    }

    return {
        serverUrl: serverUrl,
        requestHeader: {
            'Content-Type': content_type,
            'Accept-Language': DEFAULT_LANGUAGE,
            Authorization: `Bearer ${idToken}`
        }
    };
}

async function post(url, body, isSocialServer, isMultipart) {
    const {serverUrl, requestHeader} = getIRequestProp(isSocialServer, isMultipart);
    return axios.post(serverUrl + url, body, {
        headers: requestHeader
    });
}

API调用示例

AxiosServices.post(ApiUrlServices.SOCIALS_UPDATE_LINKS(UserInfo.userId), payload, false)
    .then(response => {})
问题排查与解决方案

1. 存储介质不一致(核心问题)

拦截器中更新token后使用setSessionStorage("userData", usersData)存储,但请求封装方法getIRequestProp是从localStorage读取token,这会导致:

  • 若存在未贴出的请求拦截器,它可能会重新从localStorage读取旧token,覆盖手动设置的新值
  • 后续通过封装方法发起的请求也会拿到旧token

修复方式:统一存储介质,将拦截器中的存储代码改为:

localStorage.setItem("userData", JSON.stringify(usersData));

2. Axios Config对象不可变性问题

部分Axios版本中,error.response.config可能是冻结对象,直接修改headers属性可能不生效。需创建新的headers对象覆盖原配置:

// 替换原直接赋值的代码
error.response.config.headers = {
  ...error.response.config.headers,
  Authorization: `Bearer ${response.data.data.idToken}`
};

3. 添加防重复刷新标识,避免无限循环

在拦截器中添加自定义标识,防止同一请求多次进入401刷新逻辑:

axios.interceptors.response.use(
  (response) => {
    return response;
  },
  (error) => {
    if (error.response.status === 401) {
      // 避免重复触发刷新逻辑
      if (error.response.config._isRefreshing) {
        return Promise.reject(error);
      }
      error.response.config._isRefreshing = true;

      let usersData = JSON.parse(localStorage.getItem("userData"));
      const refreshToken = usersData.refreshToken;
      return axios
        .post(
          `${api_base_url}/auth/authentication/refresh_token`,
          JSON.stringify({
            refresh_token: refreshToken,
          })
        )
        .then((response) => {
          usersData["accessToken"] = response.data.data.accessToken;
          usersData["idToken"] = response.data.data.idToken;

          localStorage.setItem("userData", JSON.stringify(usersData));

          error.response.config.headers = {
            ...error.response.config.headers,
            Authorization: `Bearer ${response.data.data.idToken}`
          };

          // 清除标识,允许后续请求正常触发刷新
          delete error.response.config._isRefreshing;
          return axios(error.response.config);
        })
        .catch((error) => {
          delete error.response.config._isRefreshing;
          if (error.response.data.code !== "TOKEN_EXPIRED") {
            return Promise.reject(error);
          }
          localStorage.clear();
          window.location = "/login";
        });
    }
    return Promise.reject(error);
  }
);

4. 检查Axios版本兼容性

React 18项目可能使用了更高版本的Axios(如v1.x),与React 16项目中的旧版本(如v0.x)存在config处理差异:

  • 确认config.headers的结构,确保Authorization字段大小写正确(首字母大写)
  • 检查是否存在全局请求拦截器,若有需确认其逻辑是否会覆盖手动设置的Authorization头

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 07:25:24