前端接口防止重复请求实现方案

众所周知,我们在项目开发过程中难免会遇到重复请求的处理(用户快速点击按钮,触发调用接口操作),下面是实现防止重复请求的实现方案
  • 方案1

    这个方案是最容易想到也是最朴实无华的一个方案:通过使用axios拦截器,在请求拦截器中开启全屏Loading,然后在响应拦截器中将Loading关闭。

    在这里插入图片描述

    这个方案固然已经可以满足我们目前的需求,但不管三七二十一,直接搞个全屏Loading还是不太美观,何况在目前项目的接口处理逻辑中还有一些局部Loading,就有可能会出现Loading套Loading的情况,两个圈一起转,头皮发麻。

  • 方案2

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

    首先,我们要判断什么样的请求属于是相同请求

    一个请求包含的内容不外乎就是请求方法地址参数以及请求发出的页面hash。那我们是不是就可以根据这几个数据把这个请求生成一个key来作为这个请求的标识呢?

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

    有了请求的key,我们就可以在请求拦截器中把每次发起的请求给收集起来,后续如果有相同请求进来,那都去这个集合中去比对,如果已经存在了,说明就是一个重复的请求,我们就给拦截掉。当请求完成响应后,再将这个请求从集合中移除。合理,nice!

    具体实现如下:

    import axios from 'axios';
    
    const instance = axios.create({
         
         
      baseUrl: '/api/',
    });
    
    // 根据请求生成对应的 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();
    
    // 添加请求拦截器
    instance.interceptors.request.use(function (config) {
         
         
      const hash = location.hash;
      // 生成请求 Key
      const reqKey = generateReqKey(config, hash);
      if (pendingRequest.has(reqKey)) {
         
         
        return Promise.reject(
<think>好的,我现在需要解决用户关于如何防止或处理API接口重复请求的问题。用户提到他们已经有前端重复提交的代码,比如遮罩层,但有时候还是没拦住,所以想在后端做处理。我需要根据提供的三个引用资料来整理解决方案。 首先,引用[1]提到Token验证的方法,这应该是常见的解决方案之一。Token验证的思路是在请求前先获取一个唯一的Token,提交时必须带上这个Token,服务器验证后使其失效,防止重复使用。这个方案需要前后端配合,前端在提交表单前先请求Token,后端生成并存储,可能用缓存如Redis。不过要注意Token的存储和过期时间,避免资源浪费。 接下来,引用[2]和[3]都讨论了后端拦截的方法,可能包括幂等性设计和请求去重。幂等性是指同一请求多次执行结果相同,比如使用唯一标识(如订单ID)。用户可以在请求中带一个唯一ID,服务器检查该ID是否已处理过。另外,请求参数去重可以通过计算参数的哈希值,并在一段时间内缓存,拒绝重复的哈希值。这需要处理时间窗口,比如设置5秒内相同请求视为重复。 还有可能提到的方案是限流和加锁,比如分布式锁,确保同一用户或IP在短时间内不能重复提交。或者使用队列异步处理,将请求排队,避免并发重复。 需要考虑每种方案的优缺点。Token验证需要前端配合,但实现相对简单;幂等性设计适合写操作,如支付,但需要业务逻辑支持;请求去重可能对参数变化敏感,需合理设置时间窗口;分布式锁可能需要额外的基础设施,如Redis。 还要注意用户提到的前端已经做了措施但失效,可能原因有网络延迟、用户恶意请求前端代码漏洞,所以后端防护是必要的。综合三个引用,应该总结出几种主要方案:Token验证、幂等性设计、请求参数去重、分布式锁、异步队列,并给出实现步骤和注意事项。 最后生成相关问题,可能涉及具体实现细节、优缺点比较、适用场景等。需要确保回答结构清晰,步骤明确,引用正确。</think>### 防止或处理API接口重复请求的解决方案 #### 1. **Token验证机制** **实现思路** - 在用户发起请求前,先向后端申请一个唯一Token(例如UUID),并将Token存储于缓存(如Redis)中,设置合理过期时间。 - 用户提交请求时必须携带该Token,后端验证Token有效性:若存在则处理请求并删除Token;若不存在则判定为重复请求,直接拒绝[^1]。 **代码示例(伪代码)** ```python def generate_token(): token = uuid4() redis.setex(token, 300, "valid") # 设置5分钟有效期 return token def handle_request(request): token = request.headers.get("X-Request-Token") if not redis.exists(token): return {"error": "重复请求"} redis.delete(token) # 处理业务逻辑 ``` **优点** - 实现简单,适用于表单提交等场景。 **缺点** - 需前端配合,对网络延迟敏感。 --- #### 2. **幂等性设计** **实现思路** - 为每个业务操作分配唯一标识(如订单ID),客户端在请求中携带该标识。 - 服务端通过数据库唯一索引或缓存记录标识,若已存在则直接返回之前的结果[^3]。 **适用场景** - 支付、订单创建等需保证唯一性的写操作。 **实现步骤** 1. 客户端生成唯一业务ID(如`order_id`)。 2. 服务端检查`order_id`是否已处理: ```sql INSERT INTO idempotent_keys (id) VALUES (:order_id) ON CONFLICT DO NOTHING; ``` 3. 若插入成功则处理请求,否则返回已缓存的结果。 --- #### 3. **请求参数去重** **实现思路** - 计算请求参数的哈希值(如MD5),并在缓存中记录该值及时间窗口(如5秒)。 - 若相同哈希值在时间窗口内重复出现,则判定为重复请求[^2]。 **代码示例** ```python def request_deduplicate(request): params_hash = md5(request.params).hexdigest() key = f"req_hash:{params_hash}" if redis.setnx(key, 1): # 仅当key不存在时设置 redis.expire(key, 5) # 处理请求 else: return {"error": "请求过于频繁"} ``` **注意事项** - 时间窗口需根据业务场景调整(如支付类设置更长)。 - 对参数顺序敏感,需规范化参数(如按字母排序)。 --- #### 4. **分布式锁** **实现思路** - 对用户或会话加锁,例如基于用户ID或IP使用Redis分布式锁,确保同一用户短时间内无法重复提交。 **实现示例** ```python from redis import Redis from redis.lock import Lock def handle_request(user_id): lock_key = f"lock:{user_id}" lock = Lock(redis, lock_key, timeout=5) if lock.acquire(blocking=False): try: # 处理业务逻辑 finally: lock.release() else: return {"error": "请勿重复提交"} ``` --- #### 5. **异步队列处理** **实现思路** - 将请求放入消息队列(如RabbitMQ、Kafka),由消费者异步处理,天然避免并发重复。 - 结合数据库状态标记(如“处理中”),防止多次消费。 --- ### 方案对比与选型建议 | 方案 | 适用场景 | 复杂度 | 依赖基础设施 | |-----------------|------------------------|--------|--------------| | Token验证 | 表单提交、短时操作 | 低 | 缓存服务 | | 幂等性设计 | 支付、订单类接口 | 中 | 数据库 | | 请求参数去重 | 参数固定的查询类接口 | 低 | 缓存服务 | | 分布式锁 | 高并发用户场景 | 高 | Redis | | 异步队列 | 允许延迟响应的业务 | 高 | 消息队列 | ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值