【问题标题】:Joi validation - how to require or optional field based on another key exists in array?Joi 验证 - 如何根据数组中存在的另一个键来要求或可选字段?
【发布时间】:2019-10-18 18:49:14
【问题描述】:

我有这个 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);

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

我该怎么做?

【问题讨论】:

    标签: node.js validation schema joi


    【解决方案1】:

    您可以使用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()
    });
    

    我只是简化了您的架构,以便于理解。

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

    例如,此对象将失败,因为 address 不存在:

    {
        contact: {
            first_name: 'a',
            last_name: 'b',
            phone: 'c'
        }
    }
    

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

    {
        contact: {
            first_name: 'a',
            last_name: 'b',
            phone: 'c'
        },
        address: {
        }
    }
    

    最后,根据定义的schema,这个对象会通过:

    {
        contact: {
            first_name: 'a',
            last_name: 'b',
            phone: 'c'
        },
        address: {
            place: 'd'
        }
    };
    

    【讨论】:

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

    感谢 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)
      })
    

    请注意,我的回答与 Soltex 的回答不同: 他将 "contact.first_name" "contact.last_name" "contact.phone" 设为:Joi.exists()。这不好,因为这样一来,即使是空对象也“存在”,然后要求用户提供“address.place”。我们不想要这样的东西,我们需要在每个字段中至少有一个字符。

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

    【讨论】:

      猜你喜欢
      • 2019-06-04
      • 2020-03-14
      • 2020-09-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-03-08
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多