axios拦截器interceptors.response执行两次或者多次

本文描述了一个在使用Vue3、axios和axios-retry进行API请求时遇到的问题,即登录页面因token过期被重复跳转。作者通过排查发现axios-retry库导致的重复执行,并提供了移除库或修改配置以避免重复请求的解决方案。
摘要由CSDN通过智能技术生成

环境

Vue3、soybean admin: “1.0.0”(native-ui: “2.38.0”)、pnpm: “8.5.3”、axios: “1.6.7”、axios-retry: “4.0.0”

问题

axios执行了拦截器 instance.interceptors.request.use、instance.interceptors.response.use方法,再做token过期(状态码为50020)需重新跳转登录页面的逻辑时,发现onBackendFail方法执行了两次,导致登录页跳转了两次,提示了两次


解决方法

1. 前提

先排查是不是重复执行了instance.interceptors方法,或者instance实例化了多次,调用了多次

2. 分析

粗略看代码发现,是因为soybean admin使用 axios-retry,让失败的请求重新发起请求,我们可以去掉这个库,不使用该功能(一般应该是网络问题会用到),但是我这边是选择更改配置:

调用axiosRetry的地方:

// 文件路径:packages\axios\src\index.ts
function createCommonRequest<ResponseData = any>(
  axiosConfig?: CreateAxiosDefaults,
  options?: Partial<RequestOption<ResponseData>>
) {
  const opts = createDefaultOptions<ResponseData>(options);

  const axiosConf = createAxiosConfig(axiosConfig);
  const instance = axios.create(axiosConf);

  const cancelTokenSourceMap = new Map<string, CancelTokenSource>();

  // config axios retry
  const retryOptions = createRetryOptions(axiosConf);
  axiosRetry(instance, retryOptions);  // 这里调用了axiosRetry,请求失败时会自动再次发起请求

  instance.interceptors.request.use(conf => {
    const config: InternalAxiosRequestConfig = { ...conf };

    // set request id
    const requestId = nanoid();
    config.headers.set(REQUEST_ID_KEY, requestId);

    // config cancel token
    const cancelTokenSource = axios.CancelToken.source();
    config.cancelToken = cancelTokenSource.token;
    cancelTokenSourceMap.set(requestId, cancelTokenSource);

    // handle config by hook
    const handledConfig = opts.onRequest?.(config) || config;

    return handledConfig;
  });

  instance.interceptors.response.use(
    async response => {
      if (opts.isBackendSuccess(response)) {
        return Promise.resolve(response);
      }

      const fail = await opts.onBackendFail(response, instance);
      if (fail) {
        return fail;
      }

      const backendError = new AxiosError<ResponseData>(
        'the backend request error',
        BACKEND_ERROR_CODE,
        response.config,
        response.request,
        response
      );

      await opts.onError(backendError);

      return Promise.reject(backendError);
    },
    async (error: AxiosError<ResponseData>) => {
      await opts.onError(error);

      return Promise.reject(error);
    }
  );

  function cancelRequest(requestId: string) {
    const cancelTokenSource = cancelTokenSourceMap.get(requestId);
    if (cancelTokenSource) {
      cancelTokenSource.cancel();
      cancelTokenSourceMap.delete(requestId);
    }
  }

  function cancelAllRequest() {
    cancelTokenSourceMap.forEach(cancelTokenSource => {
      cancelTokenSource.cancel();
    });
    cancelTokenSourceMap.clear();
  }

  return {
    instance,
    opts,
    cancelRequest,
    cancelAllRequest
  };
}

3. 解决

更改一下Options配置,还参考了一些配置例子

博客:axios-retry网友博客

原来的配置:

// 文件路径:packages\axios\src\options.ts
export function createRetryOptions(config?: Partial<CreateAxiosDefaults>) {
  const retryConfig: IAxiosRetryConfig = {
    retries: 3
  };

  Object.assign(retryConfig, config);

  return retryConfig;
}

更改后的配置:

export function createRetryOptions(config?: Partial<CreateAxiosDefaults>) {
  const retryConfig: IAxiosRetryConfig = {
    retries: 1, // 重试次数
    retryCondition: (error) => {
      // 非token失效需要重试
      return error?.response?.code !== TOKEN_EXPIRED_CODE || axiosRetry.isNetworkOrIdempotentRequestError(error);
    },
  };

  Object.assign(retryConfig, config);

  return retryConfig;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值