Flask表单如何避免用户快速点击提交按钮导致重复提交?
解决Flask表单重复提交问题
为什么重定向仍无法阻止快速点击的重复提交?
Post/Redirect/Get(PRG)模式确实能解决用户刷新页面导致的重复提交,但无法处理用户在短时间内快速点击提交按钮的场景——因为多个POST请求会在第一次请求完成重定向之前就已发送到服务器。第一次提交成功写入数据库后,后续请求会因用户名/邮箱重复被验证拦截,但仍会触发服务端请求。
可行解决方案
1. 客户端:智能控制按钮状态
不点击就禁用,而是仅在表单前端验证通过后禁用按钮,验证失败时自动恢复可用状态。在register.html的{% endblock %}前添加以下脚本:
<script> const form = document.querySelector('form'); const submitBtn = document.getElementById('submit-button'); form.addEventListener('submit', function(e) { // 检查页面是否存在Flask-WTF返回的验证错误 const hasServerErrors = document.querySelectorAll('[style="color: red"]').length > 0; if (!hasServerErrors) { submitBtn.disabled = true; submitBtn.textContent = '提交中...'; } }); // 页面加载时,若存在验证错误则确保按钮可用 window.addEventListener('load', function() { const hasServerErrors = document.querySelectorAll('[style="color: red"]').length > 0; if (hasServerErrors) { submitBtn.disabled = false; submitBtn.textContent = 'Submit'; } }); </script>
2. 服务端:失效当前CSRF令牌
利用Flask-WTF的CSRF机制,在表单提交成功后刷新令牌,让重复提交的旧令牌失效。修改app.py:
from flask_wtf.csrf import generate_csrf @app.route("/register", methods=['GET','POST']) def register(): form = RegisterForm() if form.validate_on_submit(): hashed_password = bcrypt.generate_password_hash(form.password.data) new_user = User(username=form.username.data, email=form.email.data.lower(), password=hashed_password) db.session.add(new_user) db.session.commit() # 刷新CSRF令牌,使旧令牌失效 generate_csrf() return redirect(url_for('login')) else: print(form.errors.items()) return render_template("register.html", form=form)
3. 数据库:添加唯一约束
作为最后一道防线,确保即使服务端验证失效,数据库也不会写入重复数据。修改User模型:
from sqlalchemy import UniqueConstraint class User(db.Model): id = db.Column(db.Integer, primary_key=True) username = db.Column(db.String(20), nullable=False) email = db.Column(db.String(120), nullable=False) password = db.Column(db.String(60), nullable=False) # 添加用户名和邮箱的唯一约束 __table_args__ = ( UniqueConstraint('username', name='unique_username'), UniqueConstraint('email', name='unique_email'), )
内容的提问来源于stack exchange,提问作者Cristian Cassetta
相关产品推荐
相关产品推荐

