我们用nodejs实现一些功能时,往往需要对用户输入的数据进行验证。然而,验证是一件麻烦的事情,很有可能你需要验证数据类型,长度,特定规则等等,在前端做表单验证时,我们常用的做法是使用正则,正则表达式也许可以一步到位,但是他只会给你true or false,如果想要知道数据不符合哪些条件时,那么你要进一步判断,下面和大家分享一种可读性和易用性更好的实现方法。
Joi是 hapijs 自带的数据校验模块,他已经高度封装常用的校验功能,本文就是介绍如何优雅地使用 joi 对数据进行校验。相信你会喜欢上他。便于大家理解,以登录为例,一般分两种方式:A或B (输入密码或二维码),那么 joi 的配置如下即可实现检验:
var Joi = require('joi'); var schema = Joi.object({ username: Joi.string().min(3).max(30).required(), isA: Joi.boolean(), AVal: Joi.number(), isB: Joi.boolean(), BVal: Joi.string() }) .with('isA', 'AVal') .with('isB', 'BVal') .without('isA', 'isB') .or('isA', 'isB');
以上scheme配置大致意思如下:
username: 字符串类型,长度在3至30之间,必填。
isA: 布尔类型,可选
AVal: 数字类型, 可选
isB: 布尔类型, 可选
BVal: 字符串类型, 可选
with('isA', 'AVal') //意思是,isA 和 AVal 这两字段如果填写了isA,也必须要填写AVal
with('isB', 'BVal') //道理同上
without('isA', 'isB'); //意思是 isA 和 isB 只能填写其中一个
or('isA', 'isB') //意思是 isA 和 isB 这两字段至少填写其一
Let's try
var input = { username: 'abc' } var output = Joi.validate(input, schema); //error: ValidationError: "value" must contain at least one of [isA, isB]
提示至少要填写 isA 或 isB 其中之一
再看:
var input = { username: 'abc', isA: true } var output = Joi.validate(input, schema); //error: ValidationError: "isA" missing required peer "AVal"
提示 AVal 必填
再来:
var input = { username: 'abc', isA: true, AVal: 666666 } var output = Joi.validate(input, schema); //error: null
成功通过校验
这种极简易读的表达方式让你轻松快速实现校验功能,不需要 if else。这就是配置大于编码的魅力。
joi 不仅仅作用于scheme对象,而且还可以单独使用,比如:
Joi.string().validate(666666); //error: ValidationError: "value" must be a string Joi.string().validate('hehe'); // pass
有时一些意外的字段被传进来,会导致校验不通过,但你又不在乎这些多余的字段,可以这样配置:
Joi.validate({y: 3}, {x: Joi.string()}); // [ValidationError: "y" is not allowed] Joi.validate({y: 3}, {x: Joi.string()}, {allowUnknown: true}); // pass, 配置 {allowUnknown: true}
除了表单常用的数值,布尔类型等,也可以校验函数类型:
var myObject = { a: 123, b: function () { } } var schema = { a: Joi.number().integer(), b: Joi.func() } Joi.validate(myObject, schema); //pass
除此之外,还有更多类型 Joi.any() , Joi.array() , Joi.boolean() , Joi.date() , Joi.func() , Joi.number() , Joi.object() , Joi.string()
数字 + 特定的字符串:
Joi.number().allow('a').validate('a'); // pass Joi.number().valid('a').validate('a'); // pass Joi.number().valid(['a', 'b']).validate('b'); // pass Joi.number().allow('a').validate(3); // pass
不能是数字5:
Joi.number().invalid(5).validate(5); // error
允许是任何类型:
Joi.any().validate() // pass
如果需要校验对象的子对象,那么Joi的描述也可以作为了一个子对象:
var Joi = require('joi'); var schemeAB = Joi.object({ A: Joi.string().required(), B: Joi.string().required() }); var schemeCD = Joi.object({ C: Joi.string().required(), D: schemeAB //可以作为子对象 }); var output = Joi.validate({ C: 'hehe', D: { A: 'haha', B: 'hoho' } }, schemeCD); console.log(output); //pass
Joi提供的校验条件不够用?也可以使用正则:
Joi.object({ password: Joi.string().regex(/^[a-zA-Z0-9]{3,30}$/) });
hapijs 自家的Joi 当然要无缝对接起来
var Hapi = require('hapi'); var Joi = require('Joi'); var server = new Hapi.Server(); server.connection({ port: 8000 }); server.route({ method: 'GET', path: '/', handler: function (request, reply) { if (request.query.hour && request.query.minute) { reply(request.query.hour + ':' + request.query.minute); } else { reply('time unknown'); } }, config: { validate: { query: { hour: Joi.number().min(0).max(23), minute: Joi.number().min(0).max(59) } } } }); server.start(function(err) { if (err) throw err; console.log('Server running...'); });
就是如此简单的配置就即可完成数据验证,体配置大于 调试一下:
http://127.0.0.1:8000/?hour=2&minute=3
//pass
http://127.0.0.1:8000/?hour=2&minute=300
//error
小伙伴们赶紧动手来尝试一下。
Joi 的更多用法可以参考文档: https://github.com/hapijs/joi/blob/v8.0.5/API.md