ThinkPHP防重复提交核心是服务端一次性Token+Session绑定+立即销毁闭环。需显式生成Token并嵌入表单,提交时严格校验并立即清除Session中Token,成功后必须重定向跳转,前端禁用按钮仅为体验优化。

ThinkPHP 防重复提交不能只靠前端禁用按钮或数据库唯一索引,核心是「服务端一次性 Token + Session 绑定 + 立即销毁」的闭环。Token 必须生成、嵌入、校验、清空四步到位,缺一不可。
表单页:正确生成并嵌入 Token
Token 不是配置打开就自动生效,必须显式生成并写入表单。ThinkPHP 6.x 推荐两种方式:
- 使用内置模板标签:
{__token__/}(V6.1+ 默认不启用,需在config/app.php中设'token_on' => true); - 手动控制更稳妥:控制器中调用
$this->assign('token', Str::random(32)),再存入 Session:session('submit_token', $token); - 模板中写成:
<input type="hidden" name="token" value="= $token ?>">,字段名保持与后端校验一致。
提交时:严格校验并立即销毁
Token 校验不是可选步骤,而是业务逻辑执行前的第一道闸门:
- 必须用
===严格比对,避免类型转换绕过(如"0" == false); - 校验通过后第一件事是
session('submit_token', null)或session()->delete('submit_token'),不是赋空字符串或留着不管; - 若用验证器,
__token字段默认不进$data,需单独提取:$data['__token'] = $this->request->param('__token'),否则规则不触发; - 更推荐启用中间件
think\middleware\Token,并在路由中注册,由框架统一拦截非法请求。
跳转后:防止刷新重发 POST 请求
用户提交成功后按 F5 刷新,浏览器会重发原始 POST —— 这是 PRG(Post-Redirect-Get)没做完整的表现:
立即学习“PHP免费学习笔记(深入)”;
- 控制器处理完必须用
return redirect('/success')跳转,不能直接return $this->fetch()返回当前页; - 前端可辅助增强:跳转后执行
history.replaceState({}, '', location.href),把表单页从历史栈中替换掉; - 表单页加载时检测是否为刷新进入:
if (performance.navigation?.type === 1) { document.querySelector('form').reset(); },清空表单并禁用按钮。
前端配合:禁用按钮只是体验优化,不是防重主力
按钮禁用能减少误点,但绕过 JS 极其容易,绝不能替代后端校验:
- 用
form.addEventListener('submit', e => { e.preventDefault(); btn.disabled = true; ... })统一入口; - 禁用同时改文案为「提交中…」,避免用户因无反馈而狂点;
- AJAX 提交务必在
finally中恢复按钮状态,不能只在then或catch里处理; - 不要用
onclick="return false"这类粗暴写法,它不响应回车提交和屏幕阅读器等辅助操作。



















