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

文章目录

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

需求

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

实现方案

方案一

思路:通过使用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.mzph.cn/diannao/5660.shtml

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

相关文章

CentOS-Stream-9配置vsftpd

步骤一 vim /etc/vsftpd/vsftpd.conf,anonymous_enableYES,允许匿名。 systemctl enable vsftpd 设置vsftpd开机自动启动 systemctl restart vsftpd 重启vsftpd systemctl stop firewalld 关闭防火墙,匿名访问ftp成功,普通用户…

【MySQL】驱动表、被驱动表详解。—— 性能优化。

文章目录 什么是 驱动表 和 被驱动表?哪个表是驱动表?性能优化建议: 我是一名立志把细节都说清楚的博主,欢迎【关注】🎉 ~ 原创不易, 如果有帮助 ,记得【点赞】【收藏】 哦~ ❥(^_-)~ 如有错误…

Coursera: An Introduction to American Law 学习笔记 Week 04: Constitutional Law

An Introduction to American Law 本文是 https://www.coursera.org/programs/career-training-for-nevadans-k7yhc/learn/american-law 这门课的学习笔记。 文章目录 An Introduction to American LawInstructors Week 04: Constitutional LawKey Constitutional Law TermsSup…

nginx connect 异常

1.nginx反向代理 # 测试server {listen 80;server_name local.dongpeng.com;location / {# proxy_pass http://192.168.10.131:9394;proxy_pass http://127.0.0.1:9394;}} 2.出现异常 2024/05/01 17:53:41 [error] 6#6: *1 connect() failed (111: Connection refused…

pytorch多卡分布式训练卡住的问题

最近需要模型进行多任务学习,完成代码后单卡跑是没有问题的。但是多卡跑基本上会出现卡在第一个迭代的情况。忙了一天总算是解决了。总结一下我这里的原因: 我的代码里执行了torch.distributed.all_reduce() 的操作,但这个操作只服务于一个ta…

华纳云:ubuntu中fdisk找不到硬盘怎么解决?

如果在 Ubuntu 中使用 fdisk 命令找不到硬盘,可能是由于以下几个原因导致的: 1.未正确识别硬盘:可能是因为硬盘未被正确识别或未被操作系统识别。这可能是由于硬件连接问题、硬盘故障、驱动问题等引起的。 2.需要管理员权限:在 Ub…

黑马 - websocket搭建在线聊天室

这里写自定义目录标题 一、消息推送常见方式二、websocket 是什么?三、websocket api的介绍1、客户端 (浏览器) 四、实现在线聊天室1、需求2、聊天室流程分析3、消息格式4、代码实现 一、消息推送常见方式 1、轮训方式 2、SSE(…

EasyDarwin录像存储

目录 1、安装ffmpeg 2、建立录像存储路径 3、修改EasyDarwin配置文件 4、测试 (1)推流&#x

2024年第二十一届 五一杯 (A题)大学生数学建模挑战赛 | 多目标优化问题,深度学习分析 | 数学建模完整代码解析

DeepVisionary 每日深度学习前沿科技推送&顶会论文&数学建模与科技信息前沿资讯分享,与你一起了解前沿科技知识! 本次DeepVisionary带来的是五一杯的详细解读: 完整内容可以在文章末尾全文免费领取&阅读! 第一个问题…

【Linux】进程终止

思维导图 学习内容 进程终止是进程控制里面的一个重要的知识,通过这一篇博客,我们可以学习到进程终止的概念,进程终止的三种情况,进程终止的退出码和退出信号,最后在来学习进程是如何进行终止的。 学习目标 进程终止…

诺基亚贝尔探访上海斯歌,共探创新合作新机遇

近日,上海斯歌K2 BPM迎来重要客户考察交流活动。来自诺基亚贝尔的首席数字官刘少勇一行莅临了上海斯歌K2 BPM 的武汉研发中心,并对上海斯歌在BPM业务流程管理领域的研发成果及交付能力给予了高度肯定。 此次活动不仅加深了双方的战略合作,也为…

维修家政按摩到家上门小程序开源版开发

维修家政按摩到家上门小程序开源版开发 智能定位,精准计费——全新上门服务平台功能解析。 全新上门服务平台已经上线,它拥有一系列强大的功能,旨在为用户提供最便捷、最个性化的服务体验。 !](https://img-blog.csdnimg.cn/direct/86195e…

Linux 查看主机内存,CPU使用率的两种推荐指令top ,free 指令解读

top 指令 top 指令是 Linux 系统中一个实时显示系统状态的动态视图工具,主要用于监控系统的整体运行状态,包括CPU使用率、内存使用情况、运行中的进程信息等。它是一个非常强大的命令行工具,常用于性能分析和故障排查。下面是一些基本的使用…

【QEMU系统分析之实例篇(八)】

系列文章目录 第八章 QEMU系统仿真的机器创建分析实例 文章目录 系列文章目录第八章 QEMU系统仿真的机器创建分析实例 前言一、QEMU是什么?二、QEMU系统仿真的机器创建分析实例1.系统仿真的命令行参数2.目标机器创建过程3.输出分析 总结 前言 本文以 QEMU 8.2.2 为…

WCH RISC CH32V303RCT6 单片机的SDI Printf 虚拟串口功能 类似SEGGER RTT打印功能 简单分析

参考: 有关于 SDI printf 更多的信息和资料吗? 关于 CH32 系列 MCU SDI 虚拟串口功能的使用 【CH32X035 评估板测评】 教你使用 SDI 接口重定向 printf SDI (Serial Data Interface) 是沁恒微电子 RISC-V 内核的私有外设接口,CH32 RISC-V 系列目前提供了…

掌握技巧:Linux下通过命令查看系统时间

在Linux系统中,系统时间是至关重要的。程序员经常需要查看系统时间来进行调试、日志记录以及任务调度等操作。 虽然大多数Linux发行版都提供了图形化界面来查看系统时间,但是使用命令行来获取系统时间更加高效和便捷。 本文将介绍几种在Linux下通过命令…

redis 高可用 Sentinel 详解

写在前面 redis 在我们日常的业务开发中是十分常见的,而redis的可用性就必须要有很高的要求,那么 redis集群的高可用由有一个或者多个 Sentinel(哨兵) 实例组成的 哨兵系统来保证的。 哨兵 由一个或者多个 Sentinel 实例组成的 Sentinel 系统可以监控任…

CRC32 循环冗余校验

实现的CRC-32算法,Java编写,使用二进制异或相除法实现。 public static int CRC322(final byte[] _bytes) {// 补充32位0byte[] bytes new byte[_bytes.length 4];System.arraycopy(_bytes, 0, bytes, 0, _bytes.length);// CRC初始值Bits crc Bits.v…

53.HarmonyOS鸿蒙系统 App(ArkTS) socket套接字连接失败无效参数--invalid argument

ark ts socket套接字连接失败无效参数--invalid argument 绑定本机真实连接的WIFI的IP,不要绑定127.0.0.1

MySQL:DQL语句和多表设计

DQL Data Query Language(数据查询语言) 用来查询数据库中表的记录 查询关键字:SELECT 在业务系统中,查询频次是远高于增删改的. 语法 SELECT #字段列表(基本查询) From #表名列表 WHERE #条件列表(条件查询) GROUP BY #分组字段列表(分组查询) HAVING #分组后条件列表 O…