HoRain云--如何在客户端处理POST请求的幂等性

  

🎬 HoRain云小助手个人主页

 🔥 个人专栏: 《Linux 系列教程》《c语言教程

⛺️生活的理想,就是为了理想的生活!


⛳️ 推荐

前些天发现了一个超棒的服务器购买网站,性价比超高,大内存超划算!忍不住分享一下给大家。点击跳转到网站。

专栏介绍

专栏名称

专栏介绍

《C语言》

本专栏主要撰写C干货内容和编程技巧,让大家从底层了解C,把更多的知识由抽象到简单通俗易懂。

《网络协议》

本专栏主要是注重从底层来给大家一步步剖析网络协议的奥秘,一起解密网络协议在运行中协议的基本运行机制!

《docker容器精解篇》

全面深入解析 docker 容器,从基础到进阶,涵盖原理、操作、实践案例,助您精通 docker。

《linux系列》

本专栏主要撰写Linux干货内容,从基础到进阶,知识由抽象到简单通俗易懂,帮你从新手小白到扫地僧。

《python 系列》

本专栏着重撰写Python相关的干货内容与编程技巧,助力大家从底层去认识Python,将更多复杂的知识由抽象转化为简单易懂的内容。

《试题库》

本专栏主要是发布一些考试和练习题库(涵盖软考、HCIE、HRCE、CCNA等)

目录

⛳️ 推荐

专栏介绍

一、前端防重复提交

1. 禁用按钮 + 状态锁定

2. 防抖(Debounce)机制

二、生成唯一请求标识(Request ID)

1. 客户端生成唯一 ID

2. 结合本地存储暂存已发送 ID

三、幂等性令牌(Idempotency Key)

1. 获取服务端令牌

2. 自动重试时复用令牌

四、状态管理 + 请求追踪

1. 使用全局状态标记请求状态

2. 请求队列(Request Queue)

五、客户端缓存响应结果

六、与服务端协作的兜底方案

总结:客户端幂等性方案对比


img


一、前端防重复提交

1. 禁用按钮 + 状态锁定
  • 实现逻辑:在请求发起时禁用按钮,直到收到响应或超时。
  • 代码示例
    const submitButton = document.getElementById('submit-btn');
    
    async function handleSubmit() {
      submitButton.disabled = true; // 锁定按钮
      try {
        const response = await fetch('/api/order', {
          method: 'POST',
          body: JSON.stringify({ orderId: '123' })
        });
        // 处理响应...
      } catch (error) {
        console.error('提交失败:', error);
      } finally {
        submitButton.disabled = false; // 解锁按钮
      }
    }
    
2. 防抖(Debounce)机制
  • 适用场景:防止用户快速多次点击触发重复请求。
  • 代码示例(使用 Lodash)
    import { debounce } from 'lodash';
    
    const debouncedSubmit = debounce(async (data) => {
      await fetch('/api/submit', { method: 'POST', body: data });
    }, 1000); // 1秒内多次调用只执行一次
    
    // 在点击事件中调用
    button.addEventListener('click', () => debouncedSubmit(formData));
    

二、生成唯一请求标识(Request ID)

1. 客户端生成唯一 ID
  • 实现逻辑:每次请求携带唯一标识(如 UUID),服务端根据该 ID 去重。
  • 代码示例
    import { v4 as uuidv4 } from 'uuid';
    
    // 生成唯一请求 ID
    const requestId = uuidv4();
    
    // 发送请求时附加到 Header 或 Body
    fetch('/api/payment', {
      method: 'POST',
      headers: { 'X-Request-ID': requestId },
      body: JSON.stringify({ amount: 100 })
    });
    
2. 结合本地存储暂存已发送 ID
  • 实现逻辑:将已发送的请求 ID 暂存到内存或 localStorage,避免页面刷新后重复。
    const sentRequests = new Set();
    
    function sendRequest(data) {
      const requestId = uuidv4();
      if (sentRequests.has(requestId)) return;
    
      sentRequests.add(requestId);
      fetch('/api/submit', {
        method: 'POST',
        body: JSON.stringify({ ...data, requestId })
      }).finally(() => sentRequests.delete(requestId));
    }
    

三、幂等性令牌(Idempotency Key)

1. 获取服务端令牌
  • 流程
    1. 客户端先请求一个临时令牌(如 /api/get-token)。
    2. 提交 POST 请求时携带该令牌。
    3. 服务端校验令牌有效性后处理请求,并标记令牌为已使用。
  • 代码示例
    // 1. 获取令牌
    const { token } = await fetch('/api/get-token').then(res => res.json());
    
    // 2. 提交请求时附加令牌
    fetch('/api/create-order', {
      method: 'POST',
      headers: { 'Idempotency-Key': token },
      body: JSON.stringify({ productId: '789' })
    });
    
2. 自动重试时复用令牌
  • 场景:网络失败后重试请求时,使用相同的令牌。
    let idempotencyKey = localStorage.getItem('last_key') || uuidv4();
    
    function retryRequest() {
      fetch('/api/retry', {
        method: 'POST',
        headers: { 'Idempotency-Key': idempotencyKey },
        body: data
      }).then(() => {
        localStorage.removeItem('last_key');
      }).catch(() => {
        localStorage.setItem('last_key', idempotencyKey);
      });
    }
    

四、状态管理 + 请求追踪

1. 使用全局状态标记请求状态
  • 实现逻辑:通过 Redux、Vuex 或 React Context 跟踪请求状态。
  • 示例(React + Context)
    const { isSubmitting, setSubmitting } = useRequestContext();
    
    const handleSubmit = async () => {
      if (isSubmitting) return; // 阻止重复提交
      setSubmitting(true);
    
      await fetch('/api/submit', { method: 'POST' });
      setSubmitting(false);
    };
    
2. 请求队列(Request Queue)
  • 场景:严格保证请求顺序,避免并发冲突。
    const requestQueue = [];
    let isProcessing = false;
    
    function enqueueRequest(request) {
      requestQueue.push(request);
      if (!isProcessing) processQueue();
    }
    
    async function processQueue() {
      isProcessing = true;
      while (requestQueue.length > 0) {
        const request = requestQueue.shift();
        await fetch(request.url, request.options);
      }
      isProcessing = false;
    }
    

五、客户端缓存响应结果

  • 逻辑:对相同参数的请求,直接返回缓存结果。
  • 代码示例
    const responseCache = new Map();
    
    async function cachedPost(url, data) {
      const key = JSON.stringify({ url, data });
      if (responseCache.has(key)) {
        return responseCache.get(key);
      }
    
      const response = await fetch(url, {
        method: 'POST',
        body: JSON.stringify(data)
      });
      const result = await response.json();
      responseCache.set(key, result);
      return result;
    }
    

六、与服务端协作的兜底方案

尽管客户端可减少重复提交,但最终幂等性需服务端保障:

  1. 服务端去重:通过唯一索引、请求 ID 或令牌实现。
  2. 幂等性接口设计:如使用 PUT 替代 POST 更新资源。

总结:客户端幂等性方案对比

方案适用场景优点缺点
禁用按钮 + 状态锁定用户触发的重复提交简单易实现无法防止网络自动重试
唯一请求 ID需严格去重的关键操作(如支付)强一致性需服务端支持
幂等性令牌高安全要求的业务服务端可控,安全性高增加一次令牌获取请求
请求队列需顺序执行的批量操作保证顺序实现复杂度高

建议根据业务场景选择组合方案(如 防重复提交按钮 + 唯一请求 ID),并确保服务端实现幂等性逻辑。​​​​​​​

❤️❤️❤️本人水平有限,如有纰漏,欢迎各位大佬评论批评指正!😄😄😄

💘💘💘如果觉得这篇文对你有帮助的话,也请给个点赞、收藏下吧,非常感谢!👍 👍 👍

🔥🔥🔥Stay Hungry Stay Foolish 道阻且长,行则将至,让我们一起加油吧!🌙🌙🌙

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值