You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.28 16:37:31