Flask-React注册页提交表单产生两次请求的问题排查
问题分析与解决方案
问题现象
- 首次提交合法注册信息时,触发两次请求:POST返回500失败(payload可见),OPTIONS返回200成功(无响应数据),且前端表单字段未清除。
- 再次提交相同信息(因用户名已存在被后端拒绝)时,仅产生POST请求,响应提示注册失败,表单字段正常清除。
- 核心疑惑:未定义OPTIONS方法却出现该请求,且两次提交表现差异明显,已确认仅调用一次CORS。
后端代码
@auth.route('/sign-up', methods=['POST']) @cross_origin() def sign_up(): data = request.json email = data.get('email') username = data.get('username') password1 = data.get('password1') password2 = data.get('password2') #### Validating Infomation # 检查用户是否已存在 user = session.query(models.User).filter_by(username=username).first() print(user) if user: flash('Email already exists.', category='error') return jsonify({"error": "Signup unsuccessful"}), 500 if len(email) < 4: flash('Email must be greater than 3 characters', category='error') return jsonify({"error": "Signup unsuccessful"}), 500 elif len(username) < 4: flash('Username must be greater than 3 characters', category='error') return jsonify({"error": "Signup unsuccessful"}), 500 elif password1 != password2: flash('Passwords don\'t match.', category='error') return jsonify({"error": "Signup unsuccessful"}), 500 elif len(password1) < 7: flash('Password must be at least 7 characters', category='error') return jsonify({"error": "Signup unsuccessful"}), 500 else: # 添加用户到数据库 newUser = models.User(email=email, username=username, password=generate_password_hash(password1, method='scrypt')) session.add(newUser) session.commit() login_user(newUser, remember=True) flash('Account created!', category='success') return jsonify({ "email": newUser.email, "username": newUser.username, "password": newUser.password }), 200
前端代码
const SignUp = () => { const [email, setEmail] = useState(""); const [username, setUsername] = useState(""); const [password1, setPassword1] = useState(""); const [password2, setPassword2] = useState(""); const handleSubmit = async (e) => { e.preventDefault(); const data = { email, username, password1, password2, }; try { const response = await fetch('http://localhost:5000/sign-up', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(data), }) if (response.ok) { const jsonData = await response.json(); console.log(jsonData) setEmail("RIGHT"); } else { setEmail("WRONG"); console.error('Error:'); } setUsername(""); setPassword1(""); setPassword2(""); } catch (error) { console.error('Error:', error); } }; return ( <div> <div className = "signUpTitle"> Begin your workout by signing up today! </div> <div className = "signUpCreds"> <p> <label for = "signUpUsername"> Choose a Username (8-16 characters long): </label> <input type = "text" id = "signUpUsername" name = "signUpUsername" value={username} onChange={(e) => setUsername(e.target.value)} /> </p> <p> <label for = "signUpEmail"> Enter your email: </label> <input type = "text" id = "signUpEmail" name = "signUpEmail" value = {email} onChange={(e) => setEmail(e.target.value)} /> </p> <p> <label for = "signUpPassword"> Choose a Password (8-16 characters long): </label> <input type = "password" id = "signUpPassword" name = "signUpPassword" value = {password1} onChange={(e) => setPassword1(e.target.value)} /> </p> <p> <label for = "signUpPassword2"> Confirm Password: </label> <input type = "password" id = "signUpPassword2" name = "signUpPassword2" value = {password2} onChange={(e) => setPassword2(e.target.value)} /> </p> </div> <div className = "submitSignUp" onClick={handleSubmit}> <input type="submit" value="Sign Up Now!" /> </div> </div> ) } export default SignUp
问题原因与解决
1. OPTIONS请求的由来
OPTIONS是浏览器自动发起的预检请求,当跨域请求属于「非简单请求」时触发——你的请求使用了Content-Type: application/json,不属于浏览器默认允许的简单请求类型,因此浏览器会先发送OPTIONS确认后端允许该跨域请求,之后才会发送实际的POST请求。这是正常的浏览器行为,无需手动定义OPTIONS路由。
2. 首次提交POST 500+表单未清除的原因
- 后端报错点:创建用户后调用
login_user(newUser, remember=True),前后端分离场景下,Flask-Login默认的Cookie会话机制会出现跨域兼容性问题,且如果User模型未正确实现Flask-Login要求的方法(如继承UserMixin),该方法会直接抛出异常,导致返回500状态码的HTML错误页面(而非JSON)。 - 前端表单未清除:前端
response.json()尝试解析HTML响应会报错,进入catch块,此时表单清空代码(setUsername("")等)位于try块末尾,不会被执行,因此字段保留。
3. 两次提交表现差异的原因
- 首次提交:后端抛出异常返回HTML格式的500响应,前端解析失败进入
catch,表单清空逻辑未执行。 - 再次提交:后端检查到用户名已存在,直接返回JSON格式的500响应,前端进入
else分支,表单清空逻辑正常执行。
具体修复步骤
修复后端登录逻辑:
- 前后端分离场景建议改用JWT认证,替代Flask-Login的会话机制;
- 暂时注释
login_user(newUser, remember=True),先确保注册流程正常; - 确保User模型继承
UserMixin以满足Flask-Login要求:from flask_login import UserMixin class User(UserMixin, db.Model): # 模型字段定义
修正用户存在检查的提示文本:
if user: flash('Username already exists.', category='error') # 原提示与检查逻辑不符,改为Username return jsonify({"error": "Signup unsuccessful"}), 500前端优化表单清空逻辑:
将清空代码移至finally块,确保无论请求成功、失败还是报错,都能清空表单:try { // 请求逻辑 } catch (error) { console.error('Error:', error); } finally { setEmail(""); setUsername(""); setPassword1(""); setPassword2(""); }同时移除
setEmail("RIGHT")/setEmail("WRONG")的测试代码,改用正常的状态提示组件。优化CORS配置:
全局配置CORS替代单路由配置,避免遗漏:from flask_cors import CORS CORS(app, supports_credentials=True) # 若需携带Cookie则添加supports_credentials
内容的提问来源于stack exchange,提问作者user22012522
相关产品推荐
相关产品推荐

