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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.01 03:17:38