Joi.object() 和 Joi.object().keys() 有啥区别?

Posted

技术标签:

【中文标题】Joi.object() 和 Joi.object().keys() 有啥区别?【英文标题】:What is the difference between Joi.object() and Joi.object().keys()?Joi.object() 和 Joi.object().keys() 有什么区别? 【发布时间】:2020-03-07 17:45:04 【问题描述】:

根据 Joi 文档,您可以像这样使用Joi.object()

const object = Joi.object(
    a: Joi.number().min(1).max(10).integer(),
    b: Joi.any()
);

但您也可以使用Joi.object().keys() 编写等效代码,如下所示:

const object = Joi.object().keys(
    a: Joi.number().min(1).max(10).integer(),
    b: Joi.any()
);

两者有什么区别?

【问题讨论】:

两者不等价。第一个示例定义了一个只允许ab 的模式。使用object() 的第二个示例使用Joi.any() 作为初始模式,然后通过添加ab 的定义来扩展它,同时可能继承any 的许可模式定义。你可能不想扩展any,除非你知道你正在处理开放式输入。 【参考方案1】:

如果您只编写一次架构,则无需使用.keys()。正如他们的文档所说,在向对象添加更多行(键)时使用 .keys() 是“有用的”。

Joi.object().keys([schema]) notation

这与Joi.object([schema])基本相同,但是当您想添加更多键时使用Joi.object().keys([schema])更有用(例如多次调用keys())。如果只添加一组键,可以跳过keys()方法,直接使用object()即可。

有些人喜欢使用keys() 使代码更明确(这只是样式)。

取自:https://github.com/hapijs/joi/blob/v8.0.3/API.md#joiobjectkeysschema-notation


我还发现了这个:

有很多方法可以使用 joi。 hapi 文档无法显示所有内容。仅在向对象添加键时才建议调用 keys(),因为它会创建另一个模式

取自:https://github.com/hapijs/hapi/issues/2222

【讨论】:

【参考方案2】:

如文档所述:

object.keys([schema])

设置 OR 扩展允许的对象键,其中:

schema - 可选对象,其中每个键都分配有一个 joi 类型对象。如果架构是 ,则不允许使用任何键。如果 schema 为 null 或未定义,则允许任何键。 如果架构是一个带有键的对象,则这些键将添加到任何先前定义的键中(但如果先前允许所有键,则会缩小选择范围)。

因此,通过调用Joi.object(),您首先创建一个允许任何键的架构,然后通过调用.keys([schema]),您扩展该架构(基本上与使用Joi.object([schema]) 定义新架构相同)

所以这两个是等价的:

const a = Joi.object( firstName: Joi.string() );
const b = Joi.object().keys( firstName: Joi.string() );

您还可以扩展上面创建的两个模式:

const aExtended = a.keys( lastName: Joi.string() )
const bExtended = b.keys( lastName: Joi.string() )

那该用哪一个呢?

如前面的答案所述,出于代码一致性原因,有时也使用.keys() 创建***架构,但最后我认为这是个人喜好问题。

【讨论】:

【参考方案3】:

@hapi/joi documentation 对此不是很清楚(在 v17.1.0 中)。生成的模式具有相同的值,并且它们验证相同。查看源代码,Object 类型是 Keys 类型,只有一个更改,即 object 不需要从定义它的 Any 类型复制键。

Welcome to Node.js v12.16.1.
Type ".help" for more information.
> const Joi = require('@hapi/joi')
undefined
> const util = require('util')
undefined
> const object1 = Joi.object(
...     a: Joi.number().min(1).max(10).integer(),
...     b: Joi.any()
... );
undefined
> const object2 = Joi.object().keys(
...     a: Joi.number().min(1).max(10).integer(),
...     b: Joi.any()
... );
undefined
> util.format(object1) == util.format(object2)
true
> object1.validate(a: 1, b: 1)
 value:  a: 1, b: 1  
> object2.validate(a: 1, b: 1)
 value:  a: 1, b: 1  
> object1.validate(a: 0)

value:  a: 0 ,
error: [Error [ValidationError]: "a" must be larger than or equal to 1] 
    _original:  a: 0 ,
    details: [ [Object] ]


> object2.validate(a: 0)

value:  a: 0 ,
error: [Error [ValidationError]: "a" must be larger than or equal to 1] 
    _original:  a: 0 ,
    details: [ [Object] ]


> object1.validate(a: 1, b: 1, c:1)

value:  a: 1, b: 1, c: 1 ,
error: [Error [ValidationError]: "c" is not allowed] 
    _original:  a: 1, b: 1, c: 1 ,
    details: [ [Object] ]


> object2.validate(a: 1, b: 1, c:1)

value:  a: 1, b: 1, c: 1 ,
error: [Error [ValidationError]: "c" is not allowed] 
    _original:  a: 1, b: 1, c: 1 ,
    details: [ [Object] ]


> object1.validate(a: 1)
 value:  a: 1  
> object2.validate(a: 1)
 value:  a: 1  
> object1.validate(b: 1)
 value:  b: 1  
> object2.validate(b: 1)
 value:  b: 1  
> object1.validate()
 value:  
> object2.validate()
 value:  

.append(schema).keys(schema) 之间的区别在文档中也不清楚。如果架构为空,.append(schema) 不会创建新副本,否则它只会返回来自.keys(schema) 的值。我没有发现这会产生影响的示例。

> util.format(Joi.object().keys(a:1)) == util.format(Joi.object().append(a:1))
true
> util.format(Joi.object().unknown().keys(a:1)) == util.format(Joi.object().unknown().append(a:1))
true

【讨论】:

【参考方案4】:

当只有一组键时,我们也可以不使用 keys() 来定义模式 直接在 object() 生成中定义我们的模式,如下所示:

const schema = Joi.object(
    username: Joi.string().alphanum().min(3).max(16).required(),
    password: Joi.string().regex(/^[a-zA-Z0-9]3,30$/).min(6).required()
).with('username', 'password');

那么为什么要将 keys() 与单个键集一起使用?

保持您的代码一致。在整个 Joi 文档中,甚至在单个键对象上都使用了 keys()。

使用键()

正如我们之前提到的,如果我们定义单个键集,则不必使用 keys()。此外,如果在 Joi.object() 上没有定义任何键,那么任何键都是有效的:没有规则可以使我们使用 Joi 模式测试的任何对象无效。 我们还可以选择在模式的原始定义之后添加键。以下来自 Joi 文档的示例演示了这一点:

//define base object
const base = Joi.object().keys(
    a: Joi.number(),
    b: Joi.string()
);
// add a c key onto base schema
const extended = base.keys(
    c: Joi.boolean()
);

您可能已经注意到,我们在这里定义了常量。 Joi 对象是不可变的,因此扩展基本模式将产生一个全新的对象。在这里,我们将该对象保存为扩展的常量。 我们还引入了上面的 Joi.boolean() 规则,该规则在测试复选框和其他开关时非常方便,我们希望在这些开关中使用 true 或 false 值。

【讨论】:

base.keys()base.append()有什么区别? 所以要明确一点,@Dai 留下的评论是完全错误的?

以上是关于Joi.object() 和 Joi.object().keys() 有啥区别?的主要内容,如果未能解决你的问题,请参考以下文章

Joi验证忽略嵌套键

仅允许 Joi 模式中键的特定值

Joi 验证多个条件

有没有办法合并 Joi 模式?

如何允许Joi中的任何其他键[重复]

数组的Joi验证