




表单防重复提交需前后端协同:前端提交时禁用按钮并提示,后端用一次性Token校验、数据库唯一约束或乐观锁兜底,AJAX场景还需loading状态管理与防抖。
用户点击提交后若不阻止二次点击,浏览器会发出多个请求,后端哪怕做了校验也已产生冗余压力。最直接的前端防护就是用 JavaScript 在 submit 事件中禁用按钮,并改写文案提示“提交中…”。
注意:仅靠前端禁用不可靠,必须配合后端措施;但不做这一步,用户体感差、重复提交概率陡增。
document.querySelector('form').addEventListener('submit', ...) 绑定,避免重复绑定button.disabled,防止多次触发时重复操作return false 或 event.preventDefault() 单独拦截——它可能绕过表单验证逻辑这是防重复提交最常用且有效的后端方案:session_start() 后生成唯一 $_SESSION['form_token'],输出到表单隐藏域;提交时比对并立即销毁该 token。
关键点在于「一次性」——比对成功即 unset($_SESSION['form_token']),下次再提就会因 token 为空或不匹配而拒绝。
bin2hex(random_bytes(16)) 生成,避免可预测性当业务本身有天然唯一标识(如订单号、支付流水号、用户+时间戳组合),直接在数据库加 UNIQUE 索引是最省力的兜底手段。插入失败时捕获 SQLSTATE[23000] 类错误即可返回“操作已存在”。
不适合所有场景(比如纯留言表无业务唯一键),但对订单、注册、投票等强一致性操作非常有效。
Integrity constraint violation: 1062 Duplicate entry 'xxx' for key 'uk_order_no'
try...catch 捕获 PDOException,判断 $e->getCode() === '23000'
UPDATE table SET status=1, version=version+1 WHERE id=? AND version=?,影响行为 0 行即说明已被他人更新如果表单走 AJAX(如用 fetch 或 axios),除了禁用按钮,还需管理请求状态。单纯禁用按钮在请求卡住或超时后无法恢复,用户可能强行刷新再点——这时需要更细粒度控制。
推荐做法是维护一个 isSubmitting 标志位,并在请求完成(无论 success/fail)后重置;同时对连续快速点击做简单防抖(如 500ms 内只认第一次)。
then() 和 catch() 里分别重置按钮状态,统一放在 finally 块setTimeout + clearTimeout 实现