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

文章目录

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

需求

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

实现方案

方案一

思路:通过使用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]) returnelse {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// 生成请求Keylet reqKey = generateReqKey(config, hash)if(pendingRequest.has(reqKey)) {// 如果是相同请求,在这里将请求挂起,通过发布订阅来为该请求返回结果// 这里需注意,拿到结果后,无论成功与否,都需要return Promise.reject()来中断这次请求,否则请求会正常发送至服务器let res = nulltry {// 接口成功响应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保存在configconfig.pendKey = reqKeypendingRequest.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.pendKeyif(pendingRequest.has(reqKey)) {let x = nulltry {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.pendKeyif(pendingRequest.has(reqKey)) {let x = nulltry {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.xdnf.cn/news/1409876.html

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

相关文章

详详详解动归数组常见习题(C/C++)

文章目录 最长递增数组序列(必须连续)dp[i] dp[i - 1] 1;最长递归子序列(不需要连续)dp[i] max(dp[i], dp[j] 1);俩层循环总结一维dp最长重复子数组最长公共子序列总结二维dp最终目标[3692. 最长连续公共子序列 - AcWing题库]…

【C++庖丁解牛】C++11---lambda表达式 | 包装器

🍁你好,我是 RO-BERRY 📗 致力于C、C、数据结构、TCP/IP、数据库等等一系列知识 🎄感谢你的陪伴与支持 ,故事既有了开头,就要画上一个完美的句号,让我们一起加油 目录 1. lambda表达式1.1 C98中…

ip地址与硬件地址的区别是什么

在数字世界的浩瀚海洋中,每一台联网的设备都需要一个独特的标识来确保信息的准确传输。这些标识,我们通常称之为IP地址和硬件地址。虽然它们都是用来识别网络设备的,但各自扮演的角色和所处的层次却大相径庭。虎观代理小二将带您深入了解IP地…

主成分分析在R语言中的简单应用:使用mvstats包

在数据科学领域,主成分分析(PCA)是一种广泛使用的技术,主要用于数据降维和探索性数据分析。PCA可以帮助我们发现数据中的模式,减少数据集的复杂性,同时保持数据中最重要的特征。本文将介绍如何在R语言中使用…

PID详解汇总

一、参照文章 PID的各种算法优缺点 二、位置式PID 优点:静态误差小,溢出的影响小。 缺点:计算量很大&#x

【PCL】教程 example2 3D点云之间的精确配准(FPFH特征对应关系估计变换矩阵)

这段代码主要实现了点云之间的配准功能,旨在通过估计点云的特征并找到最佳的对应关系来计算一个变换矩阵,从而可以将源点云(src)变换到目标点云(tgt)的坐标系统中。 代码功能和方法总结如下: 估…

上位机开发PyQt5(二)【单行输入框、多行输入框、按钮的信号和槽】

目录 一、单行输入框QLineEdit QLineEdit的方法: 二、多行输入框QTextEdit QTextEdit的方法 三、按钮QPushButton 四、按钮的信号与槽 信号与槽简介: 信号和槽绑定: 使用PyQt的槽函数 一、单行输入框QLineEdit QLineEdit控件可以输入…

黑马点评项目个人笔记+项目优化调整

博客须知 本篇博客内容来源与黑马点评项目实战篇-16.用户签到-实现签到功能_哔哩哔哩_bilibili,作者对视频内容进行了整合,由于记笔记时图片使用的是本地路径,所以导致博客的图片无法正常显示,如果有图片需求可以下载上方的pdf须…

程序员老鸟的 Pascal 语言菜鸟教程 -- 快速体验 Pascal

有些程序设计语言和编译器教材会以pascal语言的程序为例,这里写一个快速掌握简单应用的介绍。 1,安装 free pascal 编译器 ubuntu 22.04 直接通过 apt 源安装,此时的版本号为 3.2.2 1.1 安装 sudo apt install fp-compiler 1.2 简单测试 fpc…

【maven】pom文件详解和延伸知识

【maven】pom文件详解 【一】maven项目的pom文件详解【1】maven项目的目录结构【2】根元素和必要配置【3】父项目和parent元素【4】项目构建需要的信息【5】项目依赖相关信息(1)依赖坐标(2)依赖类型(3)依赖…

JavaScript this 上下文深度探索:综合指南涵盖隐式与显式call、apply、bind、箭头函数、构造函数等用法于多样场景

JavaScript中的this关键字代表函数执行的上下文环境,核心在于确定函数内部访问的当前对象。它根据函数调用方式动态变化,对事件处理、对象方法调用等至关重要。通过.call(), .apply(), .bind()或箭头函数控制this,可确保代码逻辑正确绑定对象…

python可视化学习笔记折线图问题-起始点问题

问题描述: 起始点的位置不对 from pyecharts.charts import Line import pyecharts.options as opts # 示例数据 x_data [1,2,3,4,5] y_data [1, 2, 3, 4, 5] # 创建 Line 图表 line Line() line.add_xaxis(x_data) line.add_yaxis("test", y_data) li…

Redis---------缓存更新,缓存穿透\雪崩\击穿

三种更新策略 内存淘汰是Redis内存的自动操作,当内存快满了就会触发内存淘汰。超时剔除则是在存储Redis时加上其有限期(expire),有限期一过就会自动删除掉。而主动更新则是自己编写代码去保持更新,所以接下来研究主动更新策略。 主动更新策略…

PS入门|网络报名证件照上传总提示审核失败是什么原因?

前言 之前小白遇到过有小伙伴报考了某个证书的考试,但在报名的过程出现了问题:证件照都是按照要求制作的,但为啥总是没有审核通过? 这个很简单:分辨率出现了问题。 啥?明明都是按照软件提示的分辨率要求制…

Python中的观察者模式及其应用

观察者模式是设计模式之一,实现一对多依赖,当主题状态变化时通知所有观察者更新。在Python中,通过自定义接口或内置模块实现观察者模式,可提高程序灵活性和扩展性,尤其适用于状态变化时触发操作的场景,如事…

Linux(ubuntu)—— 用户管理user 用户组group

一、用户 1.1、查看所有用户 cat /etc/passwd 1.2、新增用户 useradd 命令,我这里用的是2.4的命令。 然后,需要设置密码 passwd student 只有root用户才能用passwd命令设置其他用户的密码,普通用户只能够设置自己的密码 二、组 2.1查看…

【右一的开发日记】全导航,持续更新...

文章目录 📚前端【跟课笔记】🐇核心技术🐇高级技术 📚捣鼓捣鼓🐇小小案例🐇喵喵大王立大功🐇TED自用学习辅助网站🐇世界top2000计算机科学家可视化大屏🐇基于CBDB的唐代历…

中间件之异步通讯组件RabbitMQ入门

一、概述 微服务一旦拆分,必然涉及到服务之间的相互调用,目前我们服务之间调用采用的都是基于OpenFeign的调用。这种调用中,调用者发起请求后需要等待服务提供者执行业务返回结果后,才能继续执行后面的业务。也就是说调用者在调用…

HTTP/1.1、HTTP/2、HTTP/3 的演变

HTTP/1.1、HTTP/2、HTTP/3 的演变 HTTP/1.1 相比 HTTP/1.0 提高了什么性能?HTTP/2 做了什么优化?HTTP/3 做了哪些优化? HTTP/1.1 相比 HTTP/1.0 提高了什么性能? HTTP/1.1 相比 HTTP/1.0 性能上的改进: 使用长连接的…

分拣机器人也这么卷了吗?!

导语 大家好,我是智能仓储物流技术研习社的社长,老K。专注分享智能仓储物流技术、智能制造等内容。 新书《智能物流系统构成与技术实践》 智能制造-话题精读 1、西门子、ABB、汇川:2024中国工业数字化自动化50强 2、完整拆解:智能…