vue3+vant+axios失败重连+取消重复请求 取消请求

429 阅读3分钟
  1. 未登录跳登录页
  2. 失败重连
  3. 登录过期跳登录
  4. 请求之前添加请求头
// utils/require.js
import axios from "axios";
import { showToast } from "vant";
const https = axios.create({
  baseURL: "http://47.93.101.203/api/",
  timeout: 1000,
});

// 重试次数,共请求2次
https.defaults.retry = 2;
// 请求的间隙
https.defaults.retryDelay = 1000;

// 添加请求拦截器
https.interceptors.request.use(
  function (config) {
    // 在发送请求之前做些什么
    let token =
      (JSON.parse(localStorage.getItem("userInfo")) &&
        JSON.parse(localStorage.getItem("userInfo")).token) ||
      "";
    if (token != "") {
      config.headers["Authori-zation"] = "Bearer " + token;
    }
    return config;
  },
  function (error) {
    // 对请求错误做些什么
    return Promise.reject(error);
  }
);

// 添加响应拦截器
https.interceptors.response.use(
  function (response) {
    // 对响应数据做点什么
    if (response.data.status == 410000) {
      //登录过期跳转登录页
      showToast("登录过期请登录");
      setTimeout(() => {
        location.href = "/login";
      }, 500);
    }
    return response.data;
  },
  function (error) {
    // 对响应错误做点什么
    let config = error.config;
    // 判断有没有设置重试的次数 没有直接return
    if (!config || config.retry) return Promise.reject(error);
    // 设置变量以跟踪重连次数
    config.__retryCount = config.__retryCount || 0;
    // 判断是否大于当前设置的重连次数
    if (config.__retryCount>=config.__retry) {
      // 返回错误并退出自动重试
      return Promise.reject(error);
    }
    config.__retry+=1
    console.log('自动重连'+config.__retry+'次');
    
    var backoff=new Promise(function(resolve){
      setTimeout(function () {
        resolve()
      },config.retryDelay||1000)
    })

    return backoff.then(function(){
      return axios(config)
    })
  }
);
export default https;

提取重复请求

//utils/failedReconnection.js
export const failedReconnection=(error,axios)=> {
  // 超出 2xx 范围的状态码都会触发该函数。
  // 对响应错误做点什么
  let config = error.config;
  // 如果配置不存在或未设置重试选项,则拒绝
  // && 如果前面是false,后面的逻辑就不执行,前面逻辑是true的话就往后执行,只有&& 前面和后面的逻辑都是对的,才能继续。
  // || 如果前面逻辑是false的话就走后面,如果前面的逻辑是true的话,就不执行后半部分的代码了。
  if (!config || !config.retry) return Promise.reject(error);
  // 设置变量以跟踪重试次数
  config.__retryCount = config.__retryCount || 0;
  // 判断是否超过总重试次数
  if (config.__retryCount >= config.retry) {
    // 返回错误并退出自动重试
    return Promise.reject(error);
  }
  // 增加重试次数
  config.__retryCount += 1;
  console.log(config, "config ffff", config.url);

  //打印当前重试次数
  console.log(config.url + " 自动重试第" + config.__retryCount + "次");

  // 创建新的Promise
  var backoff = new Promise(function (resolve) {
    setTimeout(function () {
      resolve();
    }, config.retryDelay || 1000);
  });
  // 返回重试请求
  return backoff.then(function () {
    return axios(config);
  });
}

在utils/require.js中进行修改

import axios from "axios";
import { showToast } from "vant";
import { failedReconnection } from "./failedReconnection";
const https = axios.create({
  baseURL: "http://47.93.101.203/api/",
  timeout: 1000,
});

// 重试次数,共请求2次
https.defaults.retry = 2;
// 请求的间隙
https.defaults.retryDelay = 1000;
// 添加请求拦截器
https.interceptors.request.use(
  function (config) {
    // 在发送请求之前做些什么
    let token =
      (JSON.parse(localStorage.getItem("userInfo")) &&
        JSON.parse(localStorage.getItem("userInfo")).token) ||
      "";
    if (token != "") {
      config.headers["Authori-zation"] = "Bearer " + token;
    }
    return config;
  },
  function (error) {
    // 对请求错误做些什么
    return Promise.reject(error);
  }
);

// 添加响应拦截器
https.interceptors.response.use(
  function (response) {
    // 对响应数据做点什么
    if (response.data.status == 410000) {
      //登录过期跳转登录页
      showToast("登录过期请登录");
      setTimeout(() => {
        location.href = "/login";
      }, 500);
    }
    return response.data;
  },
  function (error) {
    // 对响应错误做点什么
   return failedReconnection(error, https);
  }
);

阻止重复请求,取消请求

//修改utils/request.js
import axios from "axios";
import { showToast } from "vant";
import { failedReconnection } from "./failedReconnection";

const https = axios.create({
  baseURL: "http://47.93.101.203/api/",
  timeout: 1000,
});
//请求列表
 let reqList = [];
// 重试次数,共请求2次
https.defaults.retry = 2;
// 请求的间隙
https.defaults.retryDelay = 1000;
// 添加请求拦截器
https.interceptors.request.use(
  function (config) {
    // 在发送请求之前做些什么
    let token =
      (JSON.parse(localStorage.getItem("userInfo")) &&
        JSON.parse(localStorage.getItem("userInfo")).token) ||
      "";
    if (token != "") {
      config.headers["Authori-zation"] = "Bearer " + token;
    }
    
    config.cancelToken = new axios.CancelToken(function (c) {
      cancel = c;
    });
     stopRepeatRequest(reqList, config.url, cancel, `${config.url} 请求被中断`)
    return config;
  },
  function (error) {
    // 对请求错误做些什么
    return Promise.reject(error);
  }
);

// 添加响应拦截器
https.interceptors.response.use(
  function (response) {
    // 对响应数据做点什么
    if (response.data.status == 410000) {
      //登录过期跳转登录页
      showToast("登录过期请登录");
      setTimeout(() => {
        location.href = "/login";
      }, 500);
    }
    return response.data;
  },
  function (error) {
    // 对响应错误做点什么
     // 增加延迟,相同请求不得在短时间内重复发送
    setTimeout(() => {
      allowRequest(reqList, error.config.url);
    }, 2000);
    
   return failedReconnection(error, https);
  }
);


/**
 * 阻止重复请求
 * @param {array} reqList - 请求缓存列表
 * @param {string} url - 当前请求地址
 * @param {function} cancel - 请求中断函数
 * @param {string} errorMessage - 请求中断时需要显示的错误信息
 */
 const stopRepeatRequest = function (reqList, url, cancel, errorMessage) {
  const errorMsg = errorMessage || "";
  for (let i = 0; i < reqList.length; i++) {
    if (reqList[i] === url) {
      cancel(errorMsg);
      return;
    }
  }
  reqList.push(url);
};

/**
 * 允许某个请求可以继续进行
 * @param {array} reqList 全部请求列表
 * @param {string} url 请求地址
 */
 const allowRequest = function (reqList, url) {
  for (let i = 0; i < reqList.length; i++) {
    if (reqList[i] === url) {
      reqList.splice(i, 1);
      break;
    }
  }
};

export default https;

提取 取消重复请求方法

//utils/stopRepeatRequest.js
export let reqList = [];
/**
 * 阻止重复请求
 * @param {array} reqList - 请求缓存列表
 * @param {string} url - 当前请求地址
 * @param {function} cancel - 请求中断函数
 * @param {string} errorMessage - 请求中断时需要显示的错误信息
 */
export const stopRepeatRequest = function (reqList, url, cancel, errorMessage) {
  const errorMsg = errorMessage || "";
  for (let i = 0; i < reqList.length; i++) {
    if (reqList[i] === url) {
      cancel(errorMsg);
      return;
    }
  }
  reqList.push(url);
};

/**
 * 允许某个请求可以继续进行
 * @param {array} reqList 全部请求列表
 * @param {string} url 请求地址
 */
export const allowRequest = function (reqList, url) {
  for (let i = 0; i < reqList.length; i++) {
    if (reqList[i] === url) {
      reqList.splice(i, 1);
      break;
    }
  }
};

修改utils/require.js

import axios from "axios";
import { showToast } from "vant";
import { failedReconnection } from "./failedReconnection";
import { reqList, stopRepeatRequest, allowRequest,} from "./stopRepeatRequest.js";
const https = axios.create({
  baseURL: "http://47.93.101.203/api/",
  timeout: 1000,
});
//请求列表
// 重试次数,共请求2次
https.defaults.retry = 2;
// 请求的间隙
https.defaults.retryDelay = 1000;
// 添加请求拦截器
https.interceptors.request.use(
  function (config) {
    // 在发送请求之前做些什么
    let token =
      (JSON.parse(localStorage.getItem("userInfo")) &&
        JSON.parse(localStorage.getItem("userInfo")).token) ||
      "";
    if (token != "") {
      config.headers["Authori-zation"] = "Bearer " + token;
    }
    
    config.cancelToken = new axios.CancelToken(function (c) {
      cancel = c;
    });
     stopRepeatRequest(reqList, config.url, cancel, `${config.url} 请求被中断`)
    return config;
  },
  function (error) {
    // 对请求错误做些什么
    return Promise.reject(error);
  }
);

// 添加响应拦截器
https.interceptors.response.use(
  function (response) {
    // 对响应数据做点什么
    if (response.data.status == 410000) {
      //登录过期跳转登录页
      showToast("登录过期请登录");
      setTimeout(() => {
        location.href = "/login";
      }, 500);
    }
    return response.data;
  },
  function (error) {
    // 对响应错误做点什么
     // 增加延迟,相同请求不得在短时间内重复发送
    setTimeout(() => {
      allowRequest(reqList, error.config.url);
    }, 2000);
    
   return failedReconnection(error, https);
  }
);

提取拦截器

//utils/interceptors.js
import { failedReconnection } from "./failedReconnection";
import {
  reqList,
  stopRepeatRequest,
  allowRequest,
} from "./stopRepeatRequest.js";

import request from "./request.js";
let { instance, axios } = request;

let time = 1000;
// 添加请求拦截器
instance.interceptors.request.use(
  function (config) {
    console.log(config.retryDelay, "config.retryDelay");
    if (config.retryDelay < time) {
      config.retryDelay = time + 500;
    }
    // 在发送请求之前做些什么
    let cancel;
    // 设置cancelToken对象
    config.cancelToken = new axios.CancelToken(function (c) {
      cancel = c;
    });
    // 阻止重复请求。当上个请求未完成时,相同的请求不会进行
    stopRepeatRequest(reqList, config.url, cancel, `${config.url} 请求被中断`);
    return config;
  },
  function (error) {
    // 对请求错误做些什么
    return Promise.reject(error);
  }
);
// 添加响应拦截器
instance.interceptors.response.use(
  function (response) {
    // 2xx 范围内的状态码都会触发该函数。
    // 对响应数据做点什么
    setTimeout(() => {
      allowRequest(reqList, response.config.url);
    }, time);
    let { data } = response;
    return data;
  },
  function (error) {
    if (axios.isCancel(error)) {
      return Promise.reject(error.message);
    }
    // 增加延迟,相同请求不得在短时间内重复发送
    setTimeout(() => {
      allowRequest(reqList, error.config.url);
    }, time);

    return failedReconnection(error, instance);
  }
);
export default instance;

修改utils/require.js

import axios from "axios";

let baseURL = import.meta.env.VITE_BSEL_URL;

const instance = axios.create({
  baseURL,
});
// 重试次数,共请求2次
instance.defaults.retry = 2;
// 请求的间隙
instance.defaults.retryDelay = 1000;

export default { instance, axios };