玲玲爱晨 發表於 2026-3-23 14:49:00

前端如何防止用户重复提交表单?4 种可靠方案(附防坑指南)

<h1 data-id="heading-0">🧑‍💻 写在开头</h1>
<p>点赞 + 收藏 === 学会🤣🤣🤣</p>
<div>
<div>
<blockquote>
<p>别再只靠“禁用按钮”了!真正的防重提交,需要前后端协同。</p>
</blockquote>
<p>在电商下单、用户注册、支付发起等关键场景中,<strong>用户连点多次“提交”按钮</strong>是再常见不过的行为。<br>
轻则造成数据库写入多条重复记录,重则导致用户被扣款两次、库存超卖——这绝不是危言耸听。</p>
<p>那么,前端该如何有效防止重复提交?</p>
<p>本文将从<strong>用户体验</strong>和<strong>系统可靠性</strong>两个维度,为你梳理 4 种主流方案,并告诉你:<strong>为什么“禁用按钮”远远不够?</strong></p>
<hr>
<h2 data-id="heading-0">方案一:提交时禁用按钮(基础但必要)</h2>
<p>最直观的做法:点击后立即禁用提交按钮。</p>

</div>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">const submitBtn = document.getElementById('submit-btn');
const form = document.getElementById('my-form');

form.addEventListener('submit', (e) =&gt; {
e.preventDefault();

if (submitBtn.disabled) return; // 防止多次触发

submitBtn.disabled = true;
submitBtn.textContent = '提交中...';

fetch('/api/submit', { method: 'POST', body: new FormData(form) })
    .then(res =&gt; res.json())
    .then(data =&gt; {
      alert('提交成功!');
    })
    .catch(err =&gt; {
      alert('提交失败,请重试');
    })
    .finally(() =&gt; {
      submitBtn.disabled = false;
      submitBtn.textContent = '提交';
    });
});</pre>
</div>
</div>
<div>
<div>
<p><strong>优点</strong>:简单、直观、提升 UX。<br>
<strong>致命缺陷</strong>:</p>
<ul>
<li>用户刷新页面后状态丢失;</li>
<li>无法阻止通过脚本、Postman 等绕过 UI 的重复请求;</li>
<li><strong>仅靠前端,防不住!</strong></li>


</ul>
<blockquote>
<p>结论:这是必备的第一道防线,但绝不能是唯一防线。</p>


</blockquote>
<hr>
<h2 data-id="heading-1">方案二:使用防重 Token(推荐!前后端协同)</h2>
<p>这才是企业级应用的标准做法。</p>
<h3 data-id="heading-2">原理:</h3>
<ol>
<li>页面加载时,后端生成一个<strong>一次性 token</strong>(如 UUID),并存入 Session 或 Redis;</li>
<li>前端在表单中携带该 token 提交;</li>
<li>后端收到请求后:
<ul>
<li>检查 token 是否存在且未使用;</li>
<li>若有效,则标记为“已使用”并处理业务;</li>
<li>若无效或已用过,直接拒绝。</li>

</ul>

</li>

</ol></div>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">&lt;!-- 表单中隐藏 token --&gt;
&lt;input type="hidden" name="antiReplayToken" value="a1b2c3d4-5678-90ef..." /&gt;</pre>
</div>
<div>
<div>
<p><strong>优势</strong>:</p>
<ul>
<li>即使用户刷新、多开标签页,每个 token 只能用一次;</li>
<li>能防御脚本刷接口、自动化工具攻击;</li>
<li>与业务解耦,通用性强。</li>
</ul>
<p>注意:</p>
<ul>
<li>Token 必须有时效性(如 5 分钟过期);</li>
<li>必须由后端生成和校验,前端不可伪造。</li>
</ul>
<hr>
<h2 data-id="heading-3">方案三:前端加锁 + 请求去重(适用于 API 场景)</h2>
<p>如果你调用的是无表单的 API(如点击“领取优惠券”按钮),可用“请求指纹”去重。</p>
</div>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">const pendingRequests = new Set();

function requestWithDedup(key, apiCall) {
if (pendingRequests.has(key)) {
    console.log('请求正在进行,忽略重复');
    return Promise.reject('Duplicate request');
}

pendingRequests.add(key);
return apiCall().finally(() =&gt; {
    pendingRequests.delete(key);
});
}

// 使用示例
document.getElementById('claim-btn').addEventListener('click', () =&gt; {
const userId = 'user_123';
requestWithDedup(`claim_${userId}`, () =&gt;
    fetch('/api/claim-coupon', { method: 'POST' })
);
});</pre>
</div>
<p>适用场景:</p>
<ul>
<li>按钮触发的独立操作(非完整表单);</li>
<li>需要防止同一用户短时间内多次触发同一操作。</li>
</ul>
<hr>
<h2 data-id="heading-4">方案四:结合 loading 状态 + 全局拦截(提升体验)</h2>
<p>在大型应用中,可借助状态管理(如 Redux、Pinia)或 Axios 拦截器统一处理。</p>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">// Axios 示例
let isSubmitting = false;

axios.interceptors.request.use(config =&gt; {
if (config.url === '/api/submit-order') {
    if (isSubmitting) throw new Error('请勿重复提交');
    isSubmitting = true;
}
return config;
});

axios.interceptors.response.use(
res =&gt; {
    if (res.config.url === '/api/submit-order') isSubmitting = false;
    return res;
},
err =&gt; {
    if (err.config?.url === '/api/submit-order') isSubmitting = false;
    return Promise.reject(err);
}
);</pre>
</div>
<blockquote>
<p>这种方式适合 SPA 应用,能覆盖所有相关请求。</p>
</blockquote>
<hr>
<h2 data-id="heading-5">最终建议:分层防御,才是王道</h2>
<p><img src="https://img2024.cnblogs.com/blog/2149129/202603/2149129-20260323144853189-1784404413.png" alt="ScreenShot_2026-03-23_144643_918" loading="lazy"></p>
<blockquote>
<p>记住:前端可以被绕过,后端必须守住底线。</p>
</blockquote>
<hr>
<h2 data-id="heading-6">结语</h2>
<p>防止重复提交,不是“加个 disabled 就完事”,而是一套纵深防御体系。<br>从用户体验到系统安全,每一步都不可或缺。</p>
<p>下次当你看到“提交中…”的按钮时,不妨想想:你的系统,真的扛得住用户狂点十次吗?</p>
<div>
<h3 id="tid-D8HBxE">如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。</h3>
</div>
<p><em><img src="https://img2024.cnblogs.com/blog/2149129/202501/2149129-20250122165814748-630765389.png" alt="" loading="lazy"></em></p>
</div>
</div><br><br>
来源:https://www.cnblogs.com/smileZAZ/p/19757791
頁: [1]
查看完整版本: 前端如何防止用户重复提交表单?4 种可靠方案(附防坑指南)