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

使用axios拦截器优化带token刷新的请求处理函数的实现方案

现有代码问题分析

当前实现存在几个明显的可优化点:

  • 每次调用handleFetchData都会注册一次响应拦截器,多次调用后会出现拦截逻辑重复执行的问题,可能导致重复触发dispatch、token重复刷新等异常行为
  • token刷新逻辑和/me接口的业务逻辑强耦合,后续其他接口如果也需要处理token过期场景无法复用这套逻辑
  • 多层Promise嵌套形成回调地狱,代码可读性差,且没有异常捕获逻辑,刷新token、重试请求失败时没有兜底处理
  • 重复编写完整接口路径、Authorization请求头,后续接口地址变更时维护成本很高
优化实现方案

我们可以把axios实例、全局拦截器统一抽离,和业务逻辑拆分,实现通用的token自动刷新能力。

第一步:抽离全局axios实例配置

单独创建request.js文件,统一配置axios实例、请求/响应拦截器:

import axios from 'axios';

// 创建统一的axios实例,配置基础地址
const authAxios = axios.create({
  baseURL: 'http://142.93.134.108:1111'
});

// 请求拦截器:自动给请求头加Authorization
authAxios.interceptors.request.use((config) => {
  const accessToken = localStorage.getItem('accessToken');
  if (accessToken) {
    config.headers.Authorization = `Bearer ${accessToken}`;
  }
  return config;
});

// 响应拦截器:统一处理token过期刷新,并重试原请求
let isRefreshing = false; // 防止同时触发多个刷新token请求
let pendingRequests = []; // 存储token刷新期间的待重试请求

authAxios.interceptors.response.use(
  (response) => {
    // 业务状态码正常直接返回结果
    if (response.data.statusCode === 200) {
      return response;
    }
    // 业务状态码异常,判定为token过期
    const originalRequest = response.config;
    if (!isRefreshing) {
      isRefreshing = true;
      // 调用刷新token接口
      return axios.post(`${authAxios.defaults.baseURL}/refresh`, {}, {
        headers: {
          Authorization: `Bearer ${localStorage.getItem('refreshToken')}`
        }
      }).then(res => {
        // 更新本地存储的token
        const { access_token: newAccessToken, refresh_token: newRefreshToken } = res.data.body;
        localStorage.setItem('accessToken', newAccessToken);
        localStorage.setItem('refreshToken', newRefreshToken);
        // 刷新token后重试之前所有 pending 的请求
        pendingRequests.forEach(cb => cb(newAccessToken));
        pendingRequests = [];
        // 重试当前触发过期的请求
        originalRequest.headers.Authorization = `Bearer ${newAccessToken}`;
        return authAxios(originalRequest);
      }).catch(err => {
        // 刷新token失败,一般是refreshToken也过期了,这里可以加跳转到登录页的逻辑
        console.error('token刷新失败,请重新登录', err);
        localStorage.removeItem('accessToken');
        localStorage.removeItem('refreshToken');
        return Promise.reject(err);
      }).finally(() => {
        isRefreshing = false;
      });
    } else {
      // 正在刷新token时,把后续请求存到pending队列
      return new Promise(resolve => {
        pendingRequests.push((newToken) => {
          originalRequest.headers.Authorization = `Bearer ${newToken}`;
          resolve(authAxios(originalRequest));
        });
      });
    }
  },
  (error) => {
    // http状态码异常的处理逻辑,可按需扩展
    return Promise.reject(error);
  }
);

export default authAxios;

第二步:简化业务函数

拆分后原来的handleFetchData只需要处理业务逻辑即可,非常简洁:

import authAxios from './request.js';

export const handleFetchData = () => async (dispatch) => {
  try {
    const response = await authAxios.get('/me');
    dispatch({ type: "USER_ME", payload: response });
  } catch (err) {
    // 按需添加请求失败的业务处理,比如提示用户登录过期
    console.error('获取用户信息失败', err);
  }
};
优化后优势
  • 拦截器全局仅注册一次,不会出现重复执行的问题
  • token刷新逻辑全局复用,所有使用authAxios实例发送的请求都会自动处理token过期场景
  • 用async/await代替嵌套Promise回调,代码可读性更高,增加了异常捕获逻辑避免页面报错
  • 统一配置接口基础地址和请求头,后续修改无需逐个调整接口

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.11 19:09:04