解决Axios取消发送之后再次发送直接报错的问题

一、取消Axios的发送的两种方式;

        1、使用AbortController的方法取消发送;
const controller = new AbortController();

axios.get('/foo/bar', {
   signal: controller.signal
}).then(res=> {
   //...
}).catch(err=>{
   console.log(err); // 打印错误消息
});
// 取消请求
controller.abort()

        这个很容易理解,就是new一个AbortController的方法,然后把它作为参数,直接在参数中传递signal字段,我们想要取消的时候,就直接调用其中的abort方法就可以了。注意,此时的请求会直接进catch方法,而不会走then

        2、使用CancelToken的方法取消发送(新版本不应该使用此方法

        这个方法在官方的文档中明确的表示了,在v0.22.0版本之后,不应该使用此方法了。我看了一下我的axiso的版本,现在已经是v1.7.5版本了(2024年8月29日),所以不应该使用此方法了。

const CancelToken = axios.CancelToken;
const source = CancelToken.source();

axios.get('/user/12345', {
  cancelToken: source.token
}).then(res=>{

}).catch(thrown =>{
  if (axios.isCancel(thrown)) {
    console.log('Request canceled', thrown.message);
  } else {
    // 处理错误
  }
});

// 取消请求(message 参数是可选的)
source.cancel('Operation canceled by the user.');

        使用方法和上面基本上一样。因为在新版本中已经不支持了,所以再次也不赘述了; 

二、已经封装了Axios的方法,取消调用的方式;

        但是在实际的项目中,大家很少会和上面一样,直接使用axios发送请求,都是对axios进行二次封装。有请求拦截器,还有返回拦截器,统一错误处理等方法;

        比如下面的方式:

import axios from "axios";

const $http = axios.create({
  baseURL: process.env.NODE_ENV == "development" ? 'http://localhost:8848/':'/',
  timeout: process.env.NODE_ENV == "development" ? 5 * 60 * 1000 : 5 * 60 * 1000 // 超时时间都改成十分钟,因为重启的接口,需要时间较长
})

// 请求拦截器
$http.interceptors.request.use(
  config => {
    // 如果传输的参数中,有baseURL字段,需要覆盖接口的url
    // 说明此接口请求地址不是默认的
    if(config.data.hasOwnProperty('baseURL') ){
      config.baseURL = config.data.baseURL;
      delete config.data.baseURL;
    } 
    return config;
  },
  error => {
    return Promise.reject(error);
  }
);

// 响应拦截器
$http.interceptors.response.use(
  response => {
    const res = response.data;
    if (res.code != 200) {
      return Promise.reject("res.des"); // 直接调用catch方法,报错;
    } else {
      return res;
    }
  },
  error => {
    // console.log('error' + error) // for debug
    return Promise.reject(error)
  }
)


export default $http;

        这里封装了一个$http的方法,然后我们的接口文件是在另一个api.js文件中,调用此方法;

import $http from "../request/index";

// 查找右侧所有的站点
export function findApp(params) {
  params.cmd = "findApp";
  return $http({
    url: "/ms/api",
    method: "post",
    contentType: "application/json",
    data: params,
  });
}

         当我们想要调用findApp方法的时候,就需要按照下面的方式:

import { findApp } from "../api/index"; // 各个接口

findApp({
    baseURL: `http://${ip}:${port}`
}).then(res=>{
    // 其他的逻辑操作
}).catch(err=>{
    ELMessage.error(err); // 提示错误信息
})

三、取消之后,再次发送的正确方式;

        因为看到官方的示例,所以第一想法是把取消发送的方法,写进了对于axios的封装中:

以下的代码是不正确的

import axios from "axios";
const controller = new AbortController();

const $http = axios.create({
  baseURL: process.env.NODE_ENV == "development" ? 'http://localhost:8848/':'/',
  timeout: process.env.NODE_ENV == "development" ? 5 * 60 * 1000 : 5 * 60 * 1000 
})

// 请求拦截器
$http.interceptors.request.use(
  config => {
    // console.log(config);
    if(config.data.hasOwnProperty('baseURL') ){
      config.baseURL = config.data.baseURL;
      delete config.data.baseURL;
    } 
    config.signal = controller.signal; // 添加取消的标识
    if(config.method == "post" && config.contentType ){
      config.data = {
        ...config.data, // 让原来的覆盖掉通用的
      }
    }
    return config;
  },
  error => {
    return Promise.reject(error);
  }
);

        这里的问题是,如果我把 这个标识符写进公共的封装的这个组件中,导致了其他的接口再次发送的时候,也会被取消,因为他们都是用的这一个实例。所以导致了,这个接口取消了,再次调用的时候,发现这个接口直接就取消了,不能再次调用了。

        所以要是想要只取消一个接口一次发送请求,需要在调用接口的时候,给当前需要取消的接口发送标识,而不是在公共中封装的代码中发送标识

四、正确代码示例

        1、添加标志位

        首先在能取消发送的接口中添加一个标志位。方便后续的程序处理;

export function findApp(params) {
  params.canCancel = "true";
  params.cmd = "findApp";
  return $http({
    url: "/ms/api",
    method: "post",
    contentType: "application/json",
    data: params,
  });
}
        2、封装方法中处理

        在封装的方法中,添加对于canCancel的处理,因为传过来的参数都是直接放进data中,需要把这个参数放在外面;

import axios from "axios";

const $http = axios.create({
  baseURL: process.env.NODE_ENV == "development" ? 'http://localhost:8848/':'/',
  timeout: process.env.NODE_ENV == "development" ? 5 * 60 * 1000 : 5 * 60 * 1000 // 超时时间都改成十分钟,因为重启的接口,需要时间较长
})

// 请求拦截器
$http.interceptors.request.use(
  config => {
    // console.log(config);
    // 如果传输的参数中,有baseURL的话,就会覆盖接口的url
    if(config.data.hasOwnProperty('baseURL') ){
      config.baseURL = config.data.baseURL;
      delete config.data.baseURL;
    } else if(config.hasOwnProperty('canCancel')) {
      config.signal = config.data.signal;
      delete config.data.signal;
      delete config.canCancel;
    }
    if(config.method == "post" && config.contentType ){
      config.data = {
        ...config.data, // 让原来的覆盖掉通用的
      }
    }
    return config;
  },
  error => {
    return Promise.reject(error);
  }
);
        3、接口中添加参数
var controller = new AbortController();
findApp({
    
    signal: controller.signal
}).then((res) => {
    // 处理业务逻辑

}).catch(err=>{
    console.log("打印错误信息", err);
})


// 如果想要取消当前的接口的方法,直接调用
const canCancelClick = () => {
    concatController.abort(); 
}

五、其他的注意事项

         需要额外的注意的一点就是,取消axios发送接口,一般都是用于前端查询类的接口。当页面跳转了,或者进行其他的操作了。当前的接口没用了,就能给取消了。

        但是如果是上传数据类的接口,比方说是上传文件的接口,如果只是单纯的前端取消接口的话,可能会导致,前端这边已经取消了,但是接口在后台已经处理成功了。会导致前后端数据不统一;这个时候就需要额外的操作了。

        最后附上一个官方的中文文档

  • 7
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值