Joi数组对象校验问询:某字段在任一对象存在时需全局必填
Hey,这两个Joi验证场景都挺实用的,我来给你逐个拆解实现方案:
问题1:数组对象的条件必填(两种场景)
我先按两种常见的理解来给你实现,你可以对应自己的实际需求:
场景A:单个对象内的依赖(若对象有字段X,则字段Y必填)
如果你的需求是数组里的某个对象,只要它包含了特定字段,那这个对象的另一个字段就必须存在(比如某个对象填了item2,那它的item3就得必填),直接用Joi内置的.when()方法就能搞定:
const Joi = require('joi'); // 定义单个对象的验证规则 const itemSchema = Joi.object({ item1: Joi.number().required(), item2: Joi.number().optional(), // 当item2存在时,item3必须必填;否则可选 item3: Joi.number().when('item2', { is: Joi.exist(), then: Joi.required(), otherwise: Joi.optional() }) }); // 数组规则 const arraySchema = Joi.array().items(itemSchema); // 测试:第一个对象有item2但没item3,会报错 const testArr1 = [{ item1: 1, item2: 2 }, { item1: 3 }]; const result1 = arraySchema.validate(testArr1); console.log(result1.error.details[0].message); // "item3" is required // 测试:对象要么没item2,要么有item2也有item3,通过 const testArr2 = [{ item1: 1 }, { item1: 3, item2: 4, item3: 5 }]; console.log(arraySchema.validate(testArr2).error); // null
场景B:数组全局的字段一致性(若有一个对象有X,所有对象都必须有X)
如果你的需求是只要数组里有一个对象包含了某个字段,那所有对象都必须包含这个字段,这其实就是你第二个问题的核心需求,我放在下面一起详细说明。
问题2:数组对象的字段一致性检查(要么都有X,要么都没有)
这个需求是要确保数组里的对象在某个字段上保持一致:要么全有,要么全没有。Joi没有直接的内置规则,但可以用.custom()自定义验证逻辑来实现,非常灵活。
直接上代码,结合你给的示例:
const Joi = require('joi'); // 定义数组验证规则 const arraySchema = Joi.array().items( Joi.object({ item1: Joi.number().required(), item2: Joi.number().optional() // 先标记为可选,后面用自定义规则强制一致性 }) ).custom((arr, helpers) => { // 检查数组中是否存在带item2的对象 const hasItem2 = arr.some(item => 'item2' in item); if (hasItem2) { // 如果有,检查所有对象是否都有item2 const missingItem2 = arr.some(item => !('item2' in item)); if (missingItem2) { // 返回自定义错误 return helpers.error('array.fieldConsistency', { field: 'item2' }); } } return arr; // 验证通过,返回原数组 }).messages({ // 自定义错误提示,更友好 'array.fieldConsistency': '所有对象必须包含字段 {{#field}},因为已有对象设置了该字段' }); // 测试你给的示例:会报错 const testArr = [{ item1: 1, item2: 2 }, { item1: 3 }]; const result = arraySchema.validate(testArr); console.log(result.error.message); // 所有对象必须包含字段 item2,因为已有对象设置了该字段 // 测试通过情况1:所有对象都有item2 const validArr1 = [{ item1: 1, item2: 2 }, { item1: 3, item2: 4 }]; console.log(arraySchema.validate(validArr1).error); // null // 测试通过情况2:所有对象都没有item2 const validArr2 = [{ item1: 1 }, { item1: 3 }]; console.log(arraySchema.validate(validArr2).error); // null
进阶优化:复用自定义规则
如果需要对多个字段做这种一致性检查,可以把逻辑封装成可复用的函数,避免重复代码:
// 封装一致性检查函数 const enforceFieldConsistency = (fieldName) => { return (arr, helpers) => { const hasField = arr.some(item => fieldName in item); if (hasField && arr.some(item => !(fieldName in item))) { return helpers.error('array.fieldConsistency', { field: fieldName }); } return arr; }; }; // 使用时可以同时校验多个字段 const arraySchema = Joi.array().items( Joi.object({ item1: Joi.number().required(), item2: Joi.number().optional(), item3: Joi.number().optional() }) ) .custom(enforceFieldConsistency('item2')) .custom(enforceFieldConsistency('item3')) .messages({ 'array.fieldConsistency': '所有对象必须包含字段 {{#field}},因为已有对象设置了该字段' });
内容的提问来源于stack exchange,提问作者user7996789
相关产品推荐
相关产品推荐

