Joi技术问题:如何在xor校验中结合使用默认值?
解决Joi中XOR校验与默认Null值的冲突问题
我完全懂你的困扰——Joi自带的.xor()方法确实有点“认死理”,只把undefined或者完全缺失的键当成“无效项”,而null会被它当作有效的存在值,这就导致你设置了.default(null)后,原本想要的“二选一”校验完全失效了。想要保留所有键(值为null或有效字符串)同时实现严格的二选一逻辑,我们完全可以用Joi自身的能力搞定,不用额外写外部处理程序。
方案一:自定义校验函数(最直观好理解)
直接给整个对象加一个自定义校验规则,明确判断a和b里只能有一个是非null的有效值:
const schema = Joi.object({ a: Joi.string().empty('').default(null), b: Joi.string().empty('').default(null), }).custom((value, helpers) => { // 统计两个属性中非null的数量 const validCount = [value.a, value.b].filter(val => val !== null).length; if (validCount !== 1) { // 用Joi内置的xor错误类型,提示更统一 return helpers.error('object.xor', { keys: 'a, b' }); } return value; }, 'XOR validation for a and b');
这个方案的逻辑很直白:
- 当你传
{ a: 'test' },b会被自动设为null,此时非null数量是1,顺利通过校验 - 传
{ a: 'test', b: 'demo' },两个都是有效字符串,非null数量为2,触发错误 - 传空对象
{},两个都是null,非null数量为0,同样触发错误
完美符合你要的“必须选一个,且所有键都保留”的需求。
方案二:用.when()条件校验实现纯Joi内置方法方案
如果你更倾向于用Joi原生的链式API,也可以通过条件判断来实现:
const schema = Joi.object({ a: Joi.string().empty('').default(null), b: Joi.string().empty('').default(null), }) // 当a是有效值(非null),b必须是null .when('a', { is: Joi.not(null), then: Joi.object({ b: Joi.valid(null) }), // 当a是null,b必须是有效字符串 otherwise: Joi.object({ b: Joi.string().empty('').required() }) }) // 反过来约束:当b是有效值,a必须是null .when('b', { is: Joi.not(null), then: Joi.object({ a: Joi.valid(null) }), });
这个方案通过双向的条件约束,确保两个属性永远只有一个是有效字符串,另一个为null,同时默认值的设置保证了所有键都会出现在最终的value里。
测试验证
用你给出的测试代码跑一下:
const { error, value } = schema.validate({ a: 'test' }); console.log(error); // undefined(校验通过) console.log(value); // { a: 'test', b: null } 完全符合预期
再测试违规场景:
const { error } = schema.validate({ a: 'test', b: 'demo' }); console.log(error.details[0].message); // 会提示b必须为null(对应方案一或二的错误提示)
内容的提问来源于stack exchange,提问作者Glenn
相关产品推荐
相关产品推荐

