Joi验证嵌套对象时报错"author.surname"不被允许的原因及解决方法
你的推测完全正确!
问题核心就出在表单输入框的name属性使用了点符号("author.surname")——HTML表单默认会把这种命名的字段提交成扁平的键值对,而非你期望的嵌套author对象。这直接导致req.body里出现了带单引号的'author.surname'和'author.firstname'键,和Joi Schema定义的嵌套结构完全不匹配,自然触发了"author.surname" is not allowed的错误(因为Joi期待的是一个author对象,而非单独的author.surname键)。
下面给你两种实用的解决办法,可根据场景选择:
方法1:修改表单的name属性为数组格式(推荐)
直接把表单里的输入框name改成数组语法,这是HTML表单提交嵌套对象的标准写法:
<!-- 原写法 --> <input type="text" name="author.surname"> <input type="text" name="author.firstname"> <!-- 修改后的写法 --> <input type="text" name="author[surname]"> <input type="text" name="author[firstname]">
这样表单提交后,后端会自动将这些字段解析成嵌套的author对象,req.body会变成你期望的结构:
{ quote: "Ozzy Osbourne's advice on how rock stars should treat young bands is wholesome as heck", author: { surname: 'tosser', firstname: 'Oscar' }, page: '', // ... 其他字段 }
这种方法无需修改后端代码,前端调整即可彻底解决问题,是最省心的方案。
方法2:后端手动转换req.body结构
如果无法修改前端表单(比如已上线的项目),可以在Joi验证前手动将扁平键转换为嵌套对象:
module.exports.citationJoi = (req, res, next) => { // 先处理req.body,将带点的键转为嵌套对象 const transformedBody = { ...req.body }; if (transformedBody['author.surname'] && transformedBody['author.firstname']) { transformedBody.author = { surname: transformedBody['author.surname'], firstname: transformedBody['author.firstname'] }; // 删除原扁平键 delete transformedBody['author.surname']; delete transformedBody['author.firstname']; } // 用处理后的对象执行验证 const memberSchema = Joi.object().keys({ quote: Joi.string().trim().required(), author: Joi.object().keys({ surname: Joi.string().trim().required(), firstname: Joi.string().trim().required() }), page: Joi.string().allow(null, '').trim(), year: Joi.string().allow(null, '').trim(), title: Joi.string().allow(null, '').trim(), publisher: Joi.string().allow(null, '').trim(), place: Joi.string().allow(null, '').trim() }); const { error } = memberSchema.validate(transformedBody); if(error) { const msg = error.details.map(el => el.message).join(","); throw new AppError(msg, 400); } else { // 将处理后的对象赋值回req.body,方便后续业务使用 req.body = transformedBody; next(); } }
这种方法适合后端兜底处理,但不如前端修改来得彻底。
最后补个小细节:你的Joi里nestedScehma拼写有误(应为nestedSchema),虽然不影响功能,但建议修正避免后续混淆~
内容的提问来源于stack exchange,提问作者inky77
相关产品推荐
相关产品推荐

