【前端】-【防止接口重复请求】

news2024/11/14 19:01:36

文章目录

  • 需求
  • 实现方案
    • 方案一
    • 方案二
    • 方案三

需求

对整个的项目都做一下接口防止重复请求的处理

实现方案

方案一

思路:通过使用axios拦截器,在请求拦截器中开启全屏Loading,然后在响应拦截器中将Loading关闭。
代码:
在这里插入图片描述
问题:在目前项目的接口处理逻辑中还有一些局部Loading,就有可能会出现Loading套Loading的情况

方案二

思路:对于同一个接口,如果传参都是一样的,一般来说都没有必要连续请求多次吧。那我们可以通过代码逻辑直接把完全相同的请求给拦截掉,不让它到达服务端
代码:

  1. 如果两个请求的请求方法,地址,参数以及请求发出的页面hash(如果是history路由,可以将pathname加入生成key)都一样,那么可以认为他们是相同请求,我们可以根据这几个数据把这个请求生成一个key来作为这个请求的唯一标识
// 根据请求生成对应的key
function generateReqKey(config, hash) {
    const { method, url, params, data } = config;
    return [method, url, JSON.stringify(params), JSON.stringify(data), hash].join("&");
}
  1. 有了请求的key,我们就可以在请求拦截器中把每次发起的请求给收集起来,后续如果有相同请求进来,那都去这个集合中去比对,如果已经存在了,说明就是一个重复的请求,我们就给拦截掉。当请求完成响应后,再将这个请求从集合中移除。
    在这里插入图片描述
    缺点:假如项目中会有一些数据字典型的接口,这些接口由不同页面调用,如果第一个页面请求的字典接口比较慢,第二个页面的接口就被拦截了,最后就会导致第二个页面逻辑错误。虽然我们生成key的时候加入了hash(不会存在不同页面调用相同数据字典型接口,后面的请求被拦截的情况),但如果我这两个请求是来自同一个页面呢?比如,一个页面同时加载两个组件,而这两个组件都需要调用某个接口时:
    在这里插入图片描述
    那么此时,后调接口的组件就无法拿到正确数据了

方案三

思路:延续方案二的思路,仍然是拦截相同请求,但这次我们不直接把请求挂掉,而是对于相同的请求我们先给它挂起,等到最先发出去的请求拿到结果回来之后,把成功或失败的结果共享给后面到来的相同请求。
在这里插入图片描述
需要注意的是:

  1. 在拿到响应结果后,返回给之前我们挂起的请求时,我们要用到发布订阅模式
  2. 对于挂起的请求,我们需要将它拦截,不能让它执行正常的请求逻辑,所以一定要在请求拦截器中通过return Promise.reject()来直接中断请求,并做一些特殊的标记,以便于在响应拦截器中进行特殊处理。
import axios from "axios"

let instance = axios.create({
    baseURL: "/api/"
})

// 发布订阅
class EventEmitter {
    constructor() {
        this.event = {}
    }
    on(type, cbres, cbrej) {
        if (!this.event[type]) {
            this.event[type] = [[cbres, cbrej]]
        } else {
            this.event[type].push([cbres, cbrej])
        }
    }

    emit(type, res, ansType) {
        if (!this.event[type]) return
        else {
            this.event[type].forEach(cbArr => {
                if(ansType === 'resolve') {
                    cbArr[0](res)
                }else{
                    cbArr[1](res)
                }
            });
        }
    }
}


// 根据请求生成对应的key
function generateReqKey(config, hash) {
    const { method, url, params, data } = config;
    return [method, url, JSON.stringify(params), JSON.stringify(data), hash].join("&");
}

// 存储已发送但未响应的请求
const pendingRequest = new Set();
// 发布订阅容器
const ev = new EventEmitter()

// 添加请求拦截器
instance.interceptors.request.use(async (config) => {
    let hash = location.hash
    // 生成请求Key
    let reqKey = generateReqKey(config, hash)
    
    if(pendingRequest.has(reqKey)) {
        // 如果是相同请求,在这里将请求挂起,通过发布订阅来为该请求返回结果
        // 这里需注意,拿到结果后,无论成功与否,都需要return Promise.reject()来中断这次请求,否则请求会正常发送至服务器
        let res = null
        try {
            // 接口成功响应
          res = await new Promise((resolve, reject) => {
                    ev.on(reqKey, resolve, reject)
                })
          return Promise.reject({
                    type: 'limiteResSuccess',
                    val: res
                })
        }catch(limitFunErr) {
            // 接口报错
            return Promise.reject({
                        type: 'limiteResError',
                        val: limitFunErr
                    })
        }
    }else{
        // 将请求的key保存在config
        config.pendKey = reqKey
        pendingRequest.add(reqKey)
    }

    return config;
  }, function (error) {
    return Promise.reject(error);
  });

// 添加响应拦截器
instance.interceptors.response.use(function (response) {
    // 将拿到的结果发布给其他相同的接口
    handleSuccessResponse_limit(response)
    return response;
  }, function (error) {
    return handleErrorResponse_limit(error)
  });

// 接口响应成功
function handleSuccessResponse_limit(response) {
      const reqKey = response.config.pendKey
    if(pendingRequest.has(reqKey)) {
      let x = null
      try {
        x = JSON.parse(JSON.stringify(response))
      }catch(e) {
        x = response
      }
      pendingRequest.delete(reqKey)
      ev.emit(reqKey, x, 'resolve')
      delete ev.reqKey
    }
}

// 接口走失败响应
function handleErrorResponse_limit(error) {
    if(error.type && error.type === 'limiteResSuccess') {
      return Promise.resolve(error.val)
    }else if(error.type && error.type === 'limiteResError') {
      return Promise.reject(error.val);
    }else{
      const reqKey = error.config.pendKey
      if(pendingRequest.has(reqKey)) {
        let x = null
        try {
          x = JSON.parse(JSON.stringify(error))
        }catch(e) {
          x = error
        }
        pendingRequest.delete(reqKey)
        ev.emit(reqKey, x, 'reject')
        delete ev.reqKey
      }
    }
      return Promise.reject(error);
}

export default instance;

问题:当上传了两个不同的文件时,只调用了一次上传接口,按理说是两个不同的请求,可为什么会被我们前面写的逻辑给拦截掉一个呢?我们打印一下请求的config:
在这里插入图片描述
可以看到,请求体data中的数据是FormData类型,而我们在生成请求key的时候,是通过JSON.stringify方法进行操作的,而对于FormData类型的数据执行该函数得到的只有{}。所以,对于文件上传,尽管我们上传了不同的文件,但它们所发出的请求生成的key都是一样的,这么一来就触发了我们前面的拦截机制。那么我们接下来我们只需要在我们原来的拦截逻辑中判断一下请求体的数据类型即可,如果含有FormData类型的数据,我们就直接放行不再关注这个请求就是了。

function isFileUploadApi(config) {
  return Object.prototype.toString.call(config.data) === "[object FormData]"
}

react直接用swr,ahook
vue用VueRequest

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1638443.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

CD-PAN复合纳米纤维膜

CD-PAN复合纳米纤维膜可能是通过某种特定的方法,如溶剂热反应或水热反应等,将CdS纳米颗粒与PAN(聚丙烯腈)纳米纤维结合起来的复合材料。 这种复合纳米纤维膜可能会继承CdS的光电性质和PAN纳米纤维的机械性能,从而在某些…

Golang | Leetcode Golang题解之第61题旋转链表

题目: 题解: func rotateRight(head *ListNode, k int) *ListNode {if k 0 || head nil || head.Next nil {return head}n : 1iter : headfor iter.Next ! nil {iter iter.Nextn}add : n - k%nif add n {return head}iter.Next headfor add > …

R语言4版本安装mvstats(纯新手)

首先下载mvstats.R文件 下载mvstats.R文件点此链接:https://download.csdn.net/download/m0_62110645/89251535 第一种方法 找到mvstats.R的文件安装位置(R语言的工作路径) getwd() 将mvstats.R保存到工作路径 在R中输入命令 source(&qu…

Day27:阻塞队列、Kafka入门、发送系统通知、显示系统

阻塞队列BlockingQueue BlockingQueue 解决线程通信的问题。阻塞方法:put、take。 生产者消费者模式 生产者:产生数据的线程。消费者:使用数据的线程。 (Thread1生产者,Thread2消费者) 实现类 ArrayBlockingQueueLinkedBlockingQueuePr…

软件工程专业就业方向及前景分析

软件工程专业作为一门应用广泛且持续发展的学科,其就业方向多样,就业前景十分乐观,以下是上大学网(www.sdaxue.com)整理的软件工程专业一些主要的就业方向及该领域的总体前景分析,供大家参考! 就业方向&…

一文理解前端如何调用后端(java)方法

阅读完文章大约需要3~5分钟 文章目录 一、什么是后端方法路径?二、ajax、axios调用后端方法总结 一、什么是后端方法路径? 这里针对的是 java 后端项目中在 controller 文件夹中的类文件,这类文件的后缀一般都会带有 controller&#xff0c…

如何远程访问连接管理器?

远程访问连接管理器是一种方便的工具,可以实现远程访问计算机和网络设备的功能。它使用户能够从任何地点连接到远程计算机,并进行文件传输、桌面共享和远程控制等操作。远程访问连接管理器不仅提供了便利性,还能提高工作效率,并为…

【Vue 2.x】学习vue之二组件

文章目录 Vue二组件第五章es6文件导入出1、导出export 组件(component)1、定义2、模块化与组件化3、组件的分类1、非单文件组件非单文件三步骤创建组件标准写法简化写法组件的嵌套非单文件的不足之处 2、单文件组件vue单文件组件的使用脚手架创建项目重点…

(学习日记)2024.05.09:UCOSIII第六十三节:常用的结构体(os.h文件)第二部分

之前的章节都是针对某个或某些知识点进行的专项讲解,重点在功能和代码解释。 回到最初开始学μC/OS-III系统时,当时就定下了一个目标,不仅要读懂,还要读透,改造成更适合中国宝宝体质的使用方式。在学完野火的教程后&a…

【JavaEE】线程的概念

文章目录 1、什么是线程2、进程和线程的区别3、多线程的概述4、在Java中实现多线程的方法1.继承Thread类2.实现Runnable接口3.使用匿名内部类来继承Thread类,实现run方法4.使用匿名内部类来实现Runnable接口,实现run方法5.使用 lambda表达式 1、什么是线…

018、Python+fastapi,第一个Python项目走向第18步:ubuntu24.04 安装cuda和pytorch环境

一、说明 我们安装了pytorch环境之后,会用yolo v9 来测试一下,看8g 显存能不能跑下来,上次用无影云电脑,4cpu8g内存直接爆了,云电脑也死机了,提示一直占用内存不释放,我自己的云电脑不能占用内…

基于alpha shapes的边缘点提取(matlab)

1、原理介绍 由Edelsbrunner H提出的alpha shapes算法是一种简单、有效的快速提取边界点算法。其克服了点云边界点形状影响的缺点,可快速准确提取边界点。如下图所示,对于任意形状的平面点云,若一个半径为a的圆,绕其进行滚动&…

类加载子系统之类的生命周期(待完善)

0、前言 文中大量图片来源于 B站 黑马程序员 0.1、类加载子系统在 JVM 中的位置 类加载器负责的事情是:加载、链接、解析 0.2、与类的生命周期相关的虚拟机参数 参数描述-XX:TraceClassLoading打印出加载且初始化的类 1、类的生命周期 堆上的变量在分配空间的时…

如何基于nginx搭建https网站

华子目录 使用nginx的http_ssl模块建立加密传输的网站查看配置文件ssl配置文件的主要参数实验:搭建nginxssl加密认证的web服务器 使用nginx的http_ssl模块建立加密传输的网站 查看 [rootserver ~]# nginx -V #查看是否有--with-http_ssl_module模块,如…

2024五一杯:煤矿深部开采冲击地压危险预测 (详细完整思路,已修改)

背景 了解即可 煤炭是中国的主要能源和重要的工业原料。然而,随着开采深度的增加,地应力增大,井下煤岩动力灾害风险越来越大,严重影响着煤矿的安全高效开采。在各类深部煤岩动力灾害事故中,冲击地压已成为威胁中国煤矿…

BigKey的危害

1.2.1、BigKey的危害 网络阻塞 对BigKey执行读请求时,少量的QPS就可能导致带宽使用率被占满,导致Redis实例,乃至所在物理机变慢 数据倾斜 BigKey所在的Redis实例内存使用率远超其他实例,无法使数据分片的内存资源达到均衡 Redis阻…

Vue---router实现路由跳转

Vue—router实现路由跳转 目录 Vue---router实现路由跳转基本使用路由跳转html实现路由跳转JS实现路由跳转 基本使用 所谓路由&#xff0c;就是将一个个组件映射到不同的路由url中 首先要将App内的内容换成router-view // App.vue <template><div id"app"…

商务谈判技巧与口才训练方法(3篇)

商务谈判技巧与口才训练方法&#xff08;3篇&#xff09; 商务谈判技巧与口才训练方法&#xff08;**篇&#xff09;&#xff1a;技巧篇 一、商务谈判技巧 明确目标&#xff1a;在谈判前&#xff0c;明确自己的谈判目标&#xff0c;并设定好底线和期望的谈判结果。 知己知彼…

AIGC技术:现状剖析与未来趋势展望

AIGC技术&#xff1a;现状剖析与未来趋势展望 随着科技的飞速进步&#xff0c;人工智能已经逐渐渗透到我们生活的方方面面。其中&#xff0c;AIGC&#xff08;人工智能生成内容&#xff09;技术更是以其独特的魅力和巨大的潜力&#xff0c;引起了业界的广泛关注。本文将深入探…

vue处理查询框清除后无法查询问题,举例为日期选择

例如 在对应的查询方法添加 //我这里获取的是date&#xff0c;如果是其他参数改为其他的即可 if (query.date && query.date.length > 2) {conditions.noedate query.date[0] || conditions.noedate;//获取开始时间conditions.twodate query.date[1] || conditi…