验证 Joi - 所有数组元素具有相同的嵌套值



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

将以下 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,它是否真的会破坏验证。

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

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()至少一个具有.results.type来自{'A', 'B', 'C', 'D'}的元素。

我学到的有趣的事情是,在Joi中,数组元素在.ref()中用点索引,就像$runs.0.results一样。

你需要使用.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"。

最新更新