如何为Joi扩展带参数的splitArray类型实现链式配置split分隔符
实现方案
这个需求可以完全实现,核心思路是把分隔符配置存储在Schema实例本身,而非依赖校验阶段传入的全局配置。Joi扩展原生支持自定义链式方法、实例级配置存储,不需要额外hack。
完整扩展代码
const JoiBase = require('joi'); const splitArrayExt = joi => ({ base: joi.array(), type: 'splitArray', // 定义Schema实例级别的配置项 flags: { separator: { default: /\s+/ // 未指定分隔符时默认按任意空白字符拆分 } }, // 类型转换逻辑:字符串转数组 coerce: (value, helpers) => { if (typeof value !== 'string') return { value }; // 直接从当前Schema读取预先配置的分隔符 const separator = helpers.schema.$_getFlag('separator'); return { value: value.split(separator) }; }, // 自定义链式方法 rules: { splitBy: { method(separator) { // 校验分隔符参数合法性 if (typeof separator !== 'string' && !(separator instanceof RegExp)) { throw new TypeError('splitBy仅支持传入字符串或正则表达式作为分隔符'); } // 将分隔符写入当前Schema实例的配置,返回新实例支持链式调用 return this.$_setFlag('separator', separator); } } } }); const Joi = JoiBase.extend(splitArrayExt);
效果验证
你预期的调用逻辑可以直接运行,结果完全符合要求:
const schema1 = Joi.splitArray().splitBy('|').items(Joi.string()); const schema2 = Joi.splitArray().splitBy(',').items(Joi.string()); console.log(schema1.validate('A|B,C').value); // [ 'A', 'B,C' ] console.log(schema2.validate('A|B,C').value); // [ 'A|B', 'C' ] // 未调用splitBy时走默认分隔符规则 const defaultSchema = Joi.splitArray().items(Joi.string()); console.log(defaultSchema.validate('hello world joi').value); // [ 'hello', 'world', 'joi' ]
核心逻辑说明
- 用扩展配置里的
flags字段存储Schema实例的独立配置,不同Schema实例的配置完全隔离,不会互相干扰 - 自定义
splitBy规则通过Joi内置的$_setFlag方法把分隔符写入当前实例,天然支持链式调用 - coerce阶段通过
helpers.schema拿到当前执行校验的Schema实例,通过$_getFlag读取预存的分隔符,不需要在validate调用时传入任何额外配置 - 非字符串输入会直接跳过拆分逻辑,交给基础的数组类型规则处理,和Joi原生数组的校验行为保持一致
内容的提问来源于stack exchange,提问作者Demiler
相关产品推荐
相关产品推荐

