【问题标题】:Validate Joi - all array elements have the same nested value验证 Joi - 所有数组元素具有相同的嵌套值
【发布时间】:2021-10-02 07:51:54
【问题描述】:

我想使用 Joi 来验证传入的 JSON 请求对象,以便每个数组元素在路径 .runs[].results.type 处具有相同的值。如果有一个元素突出,验证应该会失败。类似于array.unique 的对立面.results.type.runs[]

将以下 JSON 想象为有效输入:

{
  runs: [
    { results: { type: 'A', side: 'left' }, meta: { createdBy: 3 } },
    { results: { type: 'A', side: 'right' }, meta: { createdBy: 1 } }
  ]
}

这应该会引发验证错误:

{
  runs: [
    { results: { type: 'A', side: 'left' }, meta: { createdBy: 3 } },
    { results: { type: 'B', side: 'right' }, meta: { createdBy: 1 } }
  ]
}

我尝试编写一个 Joi 架构,例如:

...
  runs: Joi.array()
    .min(1)
    .items(
      Joi.object()
        .unknown()
        .keys({
          results: Joi.object()
            .keys({
              type: Joi.string()
                .allow('A', 'B', 'C', 'D')
                .valid(Joi.ref('....', { in: true, adjust: runs => runs.map(run => run.results.type) }))
                .required(),
              side: Joi.string().allow('left', 'right')
            })
        })
    )
...

但这不起作用(我认为它以循环引用结束)。此外,即使它成功运行,我不确定如果提供了两种差异类型 AB,它是否真的会破坏验证。

【问题讨论】:

    标签: javascript node.js typescript validation joi


    【解决方案1】:

    您需要使用 .some() o .every() 函数,如果至少有 一些 元素满足条件,或者如果 每个 元素满足条件,则返回一个条件:

    1) 使用.some()

    const object1 = {
      runs: [
        { results: { type: 'A', side: 'left' }, meta: { createdBy: 3 } },
        { results: { type: 'A', side: 'right' }, meta: { createdBy: 1 } }
      ]};
    
    const object2 = {
    runs: [
        { results: { type: 'A', side: 'left' }, meta: { createdBy: 3 } },
        { results: { type: 'B', side: 'right' }, meta: { createdBy: 1 } }
      ]};
      
    let result1 = object1.runs.some(e => e.results.type !== 'A');
    
    console.log(result1);   // false
    
    let result2 = object2.runs.some(e => e.results.type !== 'A');
    
    console.log(result2);   // true

    2) 使用.every()

    const object1 = {
      runs: [
        { results: { type: 'A', side: 'left' }, meta: { createdBy: 3 } },
        { results: { type: 'A', side: 'right' }, meta: { createdBy: 1 } }
      ]};
    
    const object2 = {
    runs: [
        { results: { type: 'A', side: 'left' }, meta: { createdBy: 3 } },
        { results: { type: 'B', side: 'right' }, meta: { createdBy: 1 } }
      ]};
      
    let result1 = object1.runs.every(e => e.results.type === 'A');
    
    console.log(result1);   // true
    
    let result2 = object2.runs.every(e => e.results.type === 'A');
    
    console.log(result2);   // false
    • 如果您不知道目标值(在本例中为 'A'),只需取第一个类型值 -> runs[0].results.type 并用它替换 'A'。李>

    【讨论】:

    • 谢谢,这是 javascript/typescript 的有效解决方案,但是我正在寻找一种关于如何编写 Joi 模式的特定方法。此外,您提供的解决方案适用于只有两种可能类型的情况,但不适用于更多类型的情况。
    • @maricn 您可以使用 a custom Joi validation 将 javascript 与 Joi 混合使用
    【解决方案2】:

    我想我找到了一种不使用自定义函数的优雅解决方案,尽管这将是解决这个问题的一个很好的方法!

    Joi.object().keys({
      runs: Joi.array().items(
        Joi.object().keys({
          results: Joi.object().keys({
            type: Joi.string().valid(
              Joi.ref('....0.results.type')
            ).required() 
          })
        })
      ).has(
        Joi.object().keys({ 
          results: Joi.object().keys({
            type: Joi.valid('A', 'B', 'C', 'D').required()
          })
        }))
      })
    

    它基于首先确定所有runs[] 元素具有相同的.results.type 值,然后断言runs 数组.has() 至少有一个元素具有来自{'A', 'B', 'C', 'D'}.results.type

    我学到的有趣的事情是,在 Joi 中,数组元素用.ref() 中的点进行索引,比如$runs.0.results

    【讨论】:

      猜你喜欢
      • 2020-11-23
      • 2021-12-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-02-16
      • 1970-01-01
      • 1970-01-01
      • 2020-05-04
      相关资源
      最近更新 更多