Joi 验证 - 如何根据数组中存在的另一个键来要求或可选字段?

Raz*_*Raz 16 validation schema node.js joi

我有这个 Joi 架构:

  let schema = {};

  let stations = {
    contact: {
      first_name: Joi.string().min(2).max(10).regex(Regex.alphabeta, 'alphabeta').allow("").error(JoiCustomErrors),
      last_name: Joi.string().min(2).max(10).regex(Regex.alphabeta, 'alphabeta').allow("").error(JoiCustomErrors),
      phone: Joi.string().min(10).max(10).regex(Regex.num, 'num').allow("").error(JoiCustomErrors),
    },
    address: {
      place: Joi.string().min(2).max(10).regex(Regex.alphanum, 'alphanum').required().error(JoiCustomErrors),
      city: Joi.string().min(2).max(30).required().error(JoiCustomErrors),
      street: Joi.string().min(2).max(30).regex(Regex.alphabeta, 'alphabeta').required().error(JoiCustomErrors),
      house_number: Joi.string().min(1).max(6).regex(Regex.alphanum, 'alphanum').allow("").error(JoiCustomErrors)
    },
    passengers_amount: Joi.number().min(0).max(4).required().error(JoiCustomErrors),
    notes: Joi.string().min(2).max(100).regex(Regex.alphanum, 'alphanum').allow("").error(JoiCustomErrors)
  };
  schema.stations = Joi.array().items(stations).min(1).max(5).required().error(JoiCustomErrors);
Run Code Online (Sandbox Code Playgroud)

如您所见,schema.stations是一个包含最少 1 个和最多 5 个元素的数组。我希望每个元素只有在"contact.first_name" AND "contact.last_name" AND "contact.phone"存在(或根据架构正确填充)时才具有“address.place”字段。

我怎样才能做到这一点?

sol*_*tex 21

您可以使用该Joi.when()方法并创建这样的架构:

Joi.object().keys({
    contact: Joi.object().keys({
        first_name: Joi.string(),
        last_name: Joi.string(),
        phone: Joi.string(),
    }),
    address: Joi.object().keys({
        place: Joi.string(),
        city: Joi.string().min(2).max(30),
        street: Joi.string(),
        house_number: Joi.string()
    }).when('contact', {
        is: Joi.object().keys({
            first_name: Joi.exist(),
            last_name: Joi.exist(),
            phone: Joi.exist(),
        }),
        then: Joi.object({ place: Joi.required() }).required(),
        otherwise: Joi.object({ place: Joi.forbidden() })
    }),
    passengers_amount: Joi.number(),
    notes: Joi.string()
});
Run Code Online (Sandbox Code Playgroud)

我只是简化了你的shema,所以很容易理解。

基本上,我们在这里说的是,如果contact.first_namecontact.last_namecontact.phone 存在,那么addressaddress.place必需的,否则address.place禁止

例如,这个对象将失败,因为地址不存在:

{
    address: {
        first_name: 'a',
        last_name: 'b',
        phone: 'c'
    }
}
Run Code Online (Sandbox Code Playgroud)

这将失败,因为address.place不存在:

{
    address: {
        first_name: 'a',
        last_name: 'b',
        phone: 'c'
    },
    contact: {
    }
}
Run Code Online (Sandbox Code Playgroud)

最后,根据定义的模式,这个对象将通过:

{
    address: {
        first_name: 'a',
        last_name: 'b',
        phone: 'c'
    },
    contact: {
        place: 'd'
    }
};
Run Code Online (Sandbox Code Playgroud)

  • 您的解决方案很好,但需要纠正。您必须调整您的模式 - 因为在这个模式中您遇到的问题是,当用户清除“address.place”属性时,它仍然是一个空对象并且需要它。请查看我的答案,更新您自己的答案,然后我将删除我的答案并将您标记为正确答案。 (2认同)

Raz*_*Raz 5

感谢 Soltex,这就是应该使用的正确模式(但请参阅我所做的更改):

Joi.object().keys({
    contact: {
      first_name: Joi.string().min(2).max(10).regex(Regex.alphabeta, 'alphabeta').allow("").error(JoiCustomErrors),
      last_name: Joi.string().min(2).max(10).regex(Regex.alphabeta, 'alphabeta').allow("").error(JoiCustomErrors),
      phone: Joi.string().min(10).max(10).regex(Regex.num, 'num').allow("").error(JoiCustomErrors),
    },
    address: Joi.object().keys({
      place: Joi.string().min(2).max(10).regex(Regex.alphanum, 'alphanum').error(JoiCustomErrors),
      city: Joi.string().min(2).max(30).required().error(JoiCustomErrors),
      street: Joi.string().min(2).max(30).regex(Regex.alphabeta, 'alphabeta').required().error(JoiCustomErrors),
      house_number: Joi.string().min(1).max(6).regex(Regex.alphanum, 'alphanum').allow("").error(JoiCustomErrors)
    }).when('contact', {
      is: Joi.object().keys({
        first_name: Joi.string().min(1),
        last_name: Joi.string().min(1),
        phone: Joi.string().min(1),
      }),
      then: Joi.object({ place: Joi.required() }).required(),
      otherwise: Joi.object({
        place: Joi.optional().allow("")
      })
    }),
    passengers_amount: Joi.number().min(0).max(4).required().error(JoiCustomErrors),
    notes: Joi.string().min(2).max(100).regex(Regex.alphanum, 'alphanum').allow("").error(JoiCustomErrors)
  })
Run Code Online (Sandbox Code Playgroud)

请注意,我的答案更改为 Soltex 的答案:他将“contact.first_name”“contact.last_name”“contact.phone”时的值更改为:Joi.exists()。这不好,因为以这种方式,即使是一个空对象也是“存在”的,然后要求用户提供“address.place”。我们不想要这样的事情,我们在每个字段中至少需要一个字符。

另外,Soltex 答案中的其他声明是使用Joi.forbidden(),但这不是这里所需的行为 - 我们仍然需要允许用户提供位置,即使没有联系,但这不应该是强制性的 -所以我使用:Joi.Optional()代替。