为何Joi中alternatives().conditional()顺序敏感,any().when()则否?
Joi Schema 展开顺序影响验证的原因解析
场景一:alternatives().conditional() 顺序影响验证结果
问题重现
定义的Schema:
const a = { b: Joi.alternatives().conditional("a", { is: "c", then: Joi.any(), otherwise: Joi.string().valid("d", "e").required(), }), a: Joi.string().valid( "a", "b", "c", ), c: Joi.bool(), }; const b = { foo: Joi.alternatives().conditional("c", { is: true, then: Joi.any(), otherwise: Joi.alternatives().conditional("a", { switch: ["a", "b"].map((a, i) => ({ is: a, then: Joi.alternatives().conditional("b", { switch: ["d"].map((b, i) => ({ is: b, then: Joi.string().required(), ...(i === ["d"].length - 1 && { otherwise: Joi.any() }), })), }), ...(i === ["a", "b"].length - 1 && { otherwise: Joi.any(), }), })), }), }), };
验证数据:
{ "a": "a", "b": "d", "c": "true" }
- 执行
Joi.object({...a, ...b})验证通过 - 执行
Joi.object({...b, ...a})抛出「Validation Error: "foo" is required」
原因
Joi.alternatives().conditional() 的依赖字段解析是在Schema构建阶段完成的,而非验证阶段动态查找。
- 用
{...a, ...b}时,先把a里的a、b、c字段全部加入Schema,再处理b中的foo字段。此时foo的conditional依赖的字段已存在,Joi能正确解析条件:数据中c是字符串"true",会被Joi.bool()自动转换为布尔值true,触发then: Joi.any()规则,foo字段可以不存在,验证通过。 - 用
{...b, ...a}时,先处理b中的foo字段,此时Schema里还没有c、a、b这些字段(a未展开),Joi无法识别conditional的依赖字段,条件判断失效,直接触发最内层的then: Joi.string().required()规则,要求foo必填,但数据里没有该字段,因此报错。
场景二:any().when() 顺序不影响验证结果
问题重现
定义的Schema:
const a = { b: Joi.any().when("a", { is: "c", then: Joi.any(), otherwise: Joi.string().valid("d", "e").required(), }), a: Joi.string().valid("a", "b", "c"), c: Joi.bool(), }; const b = { foo: Joi.any().when("c", { is: true, then: Joi.any(), otherwise: Joi.any().when("a", { switch: ["a", "b"].map((a, i) => ({ is: a, then: Joi.any().when("b", { switch: ["d"].map((b, i) => ({ is: b, then: Joi.string().required(), ...(i === ["d"].length - 1 && { otherwise: Joi.any() }), })), }), ...(i === ["a", "b"].length - 1 && { otherwise: Joi.any(), }), })), }), }), };
无论执行 Joi.object({...a, ...b}) 还是 Joi.object({...b, ...a}),验证均通过。
原因
Joi.any().when() 是在验证阶段动态评估依赖字段的,它直接从待验证的整个数据对象中查找依赖字段,不依赖Schema构建时的字段顺序。
不管先展开a还是b,验证时when都能从数据里找到c、a、b的值,正确触发条件规则:c被转换为true,foo走then: Joi.any()规则,允许不存在,因此两种顺序都能验证通过。
内容的提问来源于stack exchange,提问作者Ivan Rubinson
相关产品推荐
相关产品推荐

