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

Axios请求拦截器async/await不生效,请求提前发送问题解决

问题

为Axios实例编写了全局请求拦截器,用于在请求前刷新过期的accessToken。拦截器使用async/await处理token刷新的异步请求,但发现token过期后的首次请求会先发送至业务接口(如/users/{id}),再执行token刷新请求,导致该请求因token无效失败,后续请求因localStorage已更新新token而成功。希望在请求阶段处理token刷新,让请求必须等待拦截器的异步操作完成后再执行,而非通过响应拦截器重试请求。

原因分析

你的拦截器代码已经使用了async/await,但存在两个核心问题导致请求未等待异步操作完成:

  1. 拦截器的错误处理函数未返回Promise.reject(error),异常时会静默跳过异步流程,直接发送原始请求
  2. 请求调用时手动携带了旧的Authorization头,且拦截器未确保覆盖该值;同时未处理多请求并发时的重复刷新问题,可能导致请求提前发送

解决方案

关键修复点

  1. 确保拦截器返回Promise:Axios会等待请求拦截器返回的Promise完成后再发送请求,async/await天然返回Promise,只需保证错误被正确抛出
  2. 覆盖请求自带的旧token:在拦截器中强制更新config.headers.Authorization,避免请求携带旧token
  3. 避免重复刷新:用全局Promise缓存正在进行的刷新请求,防止多请求并发时重复调用刷新接口
  4. 正确处理错误:拦截器错误处理函数必须返回Promise.reject(error),中断错误请求流程

修改后的拦截器代码

// 全局缓存正在进行的token刷新请求,避免重复调用
let refreshTokenPromise = null;

instance.interceptors.request.use(async (config) => {
  const tokens = JSON.parse(localStorage.getItem('users'));
  if (!tokens) {
    return config;
  }

  // 校验accessToken有效性
  const accessTokenValid = Date.now() <= (tokens?.expirationTime ?? Date.now());
  if (accessTokenValid) {
    // 强制覆盖请求自带的Authorization头
    config.headers.Authorization = `Bearer ${tokens.accessToken}`;
    return config;
  }

  const refreshToken = tokens.refreshToken;
  if (!refreshToken) {
    // 无刷新token时直接抛出错误,中断请求
    return Promise.reject(new Error('No refresh token available'));
  }

  // 复用正在进行的刷新请求
  if (refreshTokenPromise) {
    await refreshTokenPromise;
    const newTokens = JSON.parse(localStorage.getItem('users'));
    config.headers.Authorization = `Bearer ${newTokens.accessToken}`;
    return config;
  }

  try {
    // 发起token刷新请求并缓存Promise
    refreshTokenPromise = fetch(`${some-endpoint}/auth/refresh/token?token=${refreshToken}`)
      .then(response => {
        if (!response.ok) throw new Error('Token refresh failed');
        return response.json();
      })
      .then(data => {
        const newTokens = data.data.data;
        localStorage.setItem('users', JSON.stringify({
          accessToken: newTokens.access_token,
          expirationTime: Date.now() + parseInt(newTokens.expires_in) * 1000 - 600 * 1000,
          refreshToken: newTokens.refresh_token,
        }));
        return newTokens;
      })
      .finally(() => {
        // 刷新完成后清空缓存
        refreshTokenPromise = null;
      });

    const newTokens = await refreshTokenPromise;
    config.headers.Authorization = `Bearer ${newTokens.access_token}`;
    return config;
  } catch (error) {
    refreshTokenPromise = null;
    // 刷新失败可在此处跳转登录页
    return Promise.reject(error);
  }
}, (error) => {
  // 错误处理必须返回rejected Promise
  console.error(error);
  return Promise.reject(error);
});

优化请求调用方式

建议移除请求options中手动携带的Authorization头,统一由拦截器处理,彻底避免旧token干扰:

// 简化请求调用,不再手动携带Authorization
return instance.get("/users/" + id);

如果必须保留options,上述拦截器代码会自动覆盖其中的Authorization头,无需额外修改。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 08:47:38