JOI校验when条件中如何访问当前嵌套对象外部的字段?
Joi嵌套对象跨层级引用字段做条件校验的实现方案
问题根因
Joi的.when()方法默认以当前规则所属字段的直接父级对象为起点查找引用字段:
- 当
text/video字段和type同属根级对象时,直接写.when('type', ...)可以在同级找到目标字段,规则正常生效 - 当
text/video被包裹在content对象内部时,直接写.when('type', ...)会尝试查找content.type这个不存在的字段,条件永远无法匹配,因此规则失效
可行实现方案
方案1:指定引用的祖先层级(改动最小,兼容原有写法)
只需要修改.when()的引用路径,明确指定向上查找的祖先层级,即可正确拿到根级的type字段值。推荐用Joi.ref的ancestor参数明确层级,避免手写相对路径出错:
const schema = Joi.object({ type: Joi.string().valid("text", "video").required(), content: Joi.object().keys({ text: Joi.object().keys({ body: Joi.string().required(), preview_url: Joi.boolean().required() }).when(Joi.ref('type', { ancestor: 2 }), { is: "text", then: Joi.required(), otherwise: Joi.forbidden() }), video: Joi.object().keys({ url: Joi.string().required() }).when(Joi.ref('type', { ancestor: 2 }), { is: "video", then: Joi.required(), otherwise: Joi.forbidden() }) }) })
参数说明:ancestor: 2代表从当前text/video字段出发,向上回溯2级祖先:第1级是父对象content,第2级是根校验对象,最终取根对象上的type字段做条件判断。
方案2:父级对象统一配置条件规则(逻辑更易维护)
把条件判断逻辑上移到content对象的规则层,不需要在子字段中跨多层引用,后续调整规则时更集中,不容易出路径错误:
const schema = Joi.object({ type: Joi.string().valid("text", "video").required(), content: Joi.object().keys({ text: Joi.object().keys({ body: Joi.string().required(), preview_url: Joi.boolean().required() }), video: Joi.object().keys({ url: Joi.string().required() }) }).when(Joi.ref('type', { ancestor: 1 }), { switch: [ { is: "text", then: Joi.object({ text: Joi.required() }).unknown(true), otherwise: Joi.object({ text: Joi.forbidden() }) }, { is: "video", then: Joi.object({ video: Joi.required() }).unknown(true), otherwise: Joi.object({ video: Joi.forbidden() }) } ] }) })
参数说明:content的直接父级就是根对象,因此ancestor: 1即可拿到根级type值;通过switch分支匹配不同type值的规则,.unknown(true)用于保证content下其他已定义的字段不会被判定为非法字段。
校验效果
两种方案都可以完全满足需求:
- 当
type值为text时,content.text为必填项,content.video禁止传入 - 当
type值为video时,content.video为必填项,content.text禁止传入 - 校验行为和同级平铺的生效规则完全一致
内容的提问来源于stack exchange,提问作者turivishal
相关产品推荐
相关产品推荐

