You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 02:30:50