Joi验证注册页面时遇g-recaptcha-response不允许的ValidationError
解决Joi验证中"g-recaptcha-response" is not allowed的问题
这个错误的根源很明确:你的表单提交的谷歌reCAPTCHA字段是g-recaptcha-response,但你在Joi的UserSchema里定义的是recaptcha,而且Joi默认会拒绝所有Schema中未声明的字段(也就是unknown: false的默认配置),所以当请求体里包含g-recaptcha-response时,Joi就会抛出这个验证错误。
下面是具体的修复步骤:
1. 修正Joi Schema的字段名
把Schema里的recaptcha改成表单实际提交的字段名g-recaptcha-response,并且建议设置为必填项(避免用户绕过reCAPTCHA验证):
var UserSchema = Joi.object().keys({ name: Joi.string().required(), username: Joi.string().alphanum().min(4).max(30).required(), email: Joi.string().email().required(), mobile: Joi.string().required(), password: Joi.string().regex(/^[a-zA-Z0-9]{8,30}$/).required(), // 补充必填规则,密码不能为空 confirmationPassword: Joi.any().valid(Joi.ref('password')).required(), access_token: [Joi.string(), Joi.number()], 'g-recaptcha-response': Joi.string().required() // 匹配表单的reCAPTCHA字段名,并设为必填 });
2. 优化验证逻辑与reCAPTCHA校验流程
你原来的代码存在两个小问题:
- 验证reCAPTCHA的逻辑放在Joi验证之后,导致Joi先抛出未定义字段的错误
- 谷歌验证的回调还没执行完,就提前执行了
res.redirect('/login'),会导致用户还没保存就跳转
修正后的POST请求代码如下:
router.route('/register') .get(isNotAuthenticated, (req, res) => { res.render('register'); }) .post(async (req, res, next) => { try { // 开启abortEarly: false可以返回所有验证错误,方便用户一次性修正 const { error, value } = Joi.validate(req.body, UserSchema, { abortEarly: false }); if (error) { req.flash("error", "请填写有效的数据"); console.log(error.details); // 打印具体错误信息,便于调试 res.redirect('/register'); return; } // 检查用户名是否已存在 const user = await User.findOne({ username: value.username }); if (user) { req.flash('error', '用户名已被占用'); res.redirect('/register'); return; } // 验证谷歌reCAPTCHA const secretKey = '你的谷歌reCAPTCHA密钥'; // 替换为实际的密钥 const captchaResponse = value['g-recaptcha-response']; const verifyUrl = `https://google.com/recaptcha/api/siteverify?secret=${secretKey}&response=${captchaResponse}&remoteip=${req.connection.remoteAddress}`; request(verifyUrl, async (err, response, body) => { try{ body = JSON.parse(body); console.log(body); if (!body.success) { req.flash('error', 'reCAPTCHA验证失败,请重试'); res.redirect('/register'); return; } // 验证通过,创建并保存用户 const newUser = new User(value); console.log('newUser', newUser); await newUser.save(); req.flash('success', '请检查你的邮箱'); res.redirect('/login'); }catch(e){ next(e); } }); } catch (error) { next(error); } });
可选方案:允许未知字段(不推荐)
如果你不想修改Schema字段名,可以在Joi验证时添加allowUnknown: true选项,允许请求体中存在Schema未定义的字段:
const { error, value } = Joi.validate(req.body, UserSchema, { allowUnknown: true });
不过这种方式不够严谨,可能会引入不必要的字段到后续逻辑中,更推荐第一种精准匹配字段的方案。
内容的提问来源于stack exchange,提问作者Nikhil Savaliya
相关产品推荐
相关产品推荐

