You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现幂等PUT请求?避免重复点击致积分多次累加

解决重复点击导致Points多次累加的最佳实践

你现在的问题核心是前端直接计算Points并传给后端,这种方式在快速点击时会触发多个请求,每个请求都带着前端计算后的Points值,导致后端重复累加。除了禁用按钮,还有几个更可靠的方案:

1. 让后端负责原子性累加(最根本的方案)

把Points加1的逻辑从前端移到后端,前端只需要告诉后端执行"加1"操作,而不是传计算后的结果。这样即使多个请求同时到达,数据库的原子操作能保证每次只加1。

修改前端代码:

不用在前端计算points +=1,请求体里只传必要的标识:

function Block() {
  async function handleDismissButton() {
    try {
      const response = await fetch(`/api/user/${userId}`, {
        method: "PUT",
        headers: {
          "Content-Type": "application/json"
        },
        body: JSON.stringify({
          showAward: true,
          // 只传操作类型,不计算points
          action: "addPoints"
        })
      });
      const users = await response.json();
      return users;
    } catch (err) {
      return null;
    }
  } 

  return (
    <button
      role="button"
      onClick={handleDismissButton}
    >
      Click here
    </button>
  );
}

后端处理逻辑(示例SQL):

用数据库的原子更新语句,避免并发问题:

UPDATE users 
SET points = points + 1, showAward = true 
WHERE id = ?;

这种方式数据库会保证每次更新都是基于最新的字段值,不会出现重复累加。

2. 前端添加请求状态锁(简单有效)

用状态变量标记当前是否有请求在进行,后续点击直接忽略,不用强制修改按钮UI:

function Block() {
  const [isRequesting, setIsRequesting] = useState(false);

  async function handleDismissButton() {
    // 如果已有请求在进行,直接返回
    if (isRequesting) return;

    try {
      setIsRequesting(true);
      const response = await fetch(`/api/user/${userId}`, {
        method: "PUT",
        headers: {
          "Content-Type": "application/json"
        },
        body: JSON.stringify({
          showAward: true,
          points: points + 1 // 不要用+=,避免前端状态污染
        })
      });
      const users = await response.json();
      return users;
    } catch (err) {
      return null;
    } finally {
      // 请求完成后不管成功失败,都解锁
      setIsRequesting(false);
    }
  } 

  return (
    <button
      role="button"
      onClick={handleDismissButton}
    >
      Click here
    </button>
  );
}

3. 实现请求幂等性(后端兜底方案)

给每个请求生成唯一的请求ID,后端记录已经处理过的ID,遇到重复ID直接返回之前的结果,不执行累加操作:

  • 前端:每次请求时生成一个UUID作为requestId,放在请求头或请求体里
  • 后端:接收请求后,先检查requestId是否已经处理过,如果已处理则直接返回成功;如果未处理,执行累加操作并记录requestId

这种方式即使前端漏了控制,后端也能防止重复操作。

内容的提问来源于stack exchange,提问作者Omilight

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 02:32:57