如何扩展Joi的number()校验规则支持带$符号的数字字符串
问题解答
完全可以在schema定义层面实现适配,不需要在调用validate前提前清洗数据,有两种常用实现方案按需选择:
方案1:轻量组合规则(无需扩展Joi)
不需要修改Joi原生逻辑,直接组合现有校验规则,就能实现带$前缀数值的校验,效果和原生Joi.number()完全一致:
// 支持$前缀的数值校验schema const dollarNumSchema = Joi.string() // 校验格式:允许开头带$,后面是合法数字格式 .pattern(/^\$?\d+(\.\d+)?$/, 'valid numeric value with optional $ prefix') .replace(/^\$/, '') // 校验通过后自动移除开头的$ .cast('number') // 自动转换为数字类型,输出和原生number()校验无差异 .options({ convert: true }) // 测试用例 dollarNumSchema.validate("$12.34") // 校验通过,返回值为12.34 dollarNumSchema.validate("12.34") // 校验通过,返回值为12.34 dollarNumSchema.validate("$abc") // 正常返回格式错误
这个方案写起来简单,适合单一场景使用,后续还可以链式调用min()、max()、precision()等所有数字校验规则,不会有兼容问题。
方案2:扩展原生number类型(适合全局复用)
如果想直接沿用Joi.number()的调用习惯,不需要每次都写字符串组合规则,可以通过Joi的扩展能力,给原生number类型增加$前缀的自动识别逻辑:
const Joi = require('joi'); // 扩展number类型,自动处理$前缀 const CustomJoi = Joi.extend(joi => ({ type: 'number', base: joi.number(), coerce: { from: 'string', method(value) { // 输入为带$的字符串时,先移除$再走原生数字校验逻辑 if (typeof value === 'string' && value.trim().startsWith('$')) { return { value: value.trim().slice(1) } } return value } } })) // 直接调用number()即可支持带$的字符串 CustomJoi.number().validate("$12.34") // 校验通过,返回值12.34 CustomJoi.number().validate("12.34") // 原有逻辑不受影响,正常通过 CustomJoi.number().min(0).validate("$-12") // 正常触发最小值校验报错
扩展后的number类型完全保留原生所有校验能力,原有业务代码里的number规则不需要修改,只要替换成扩展后的Joi实例就能全局支持$前缀的数值输入。
内容的提问来源于stack exchange,提问作者Frank
相关产品推荐
相关产品推荐

