使用joi來驗證數據模型
- 2019 年 12 月 4 日
- 筆記
本文作者:IMWeb zzbozheng 原文出處:IMWeb社區 未經同意,禁止轉載
我們用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: 'zzbo' } var output = Joi.validate(input, schema); //error: ValidationError: "value" must contain at least one of [isA, isB]
提示至少要填寫 isA 或 isB 其中之一
再看:
var input = { username: 'zzbo', isA: true } var output = Joi.validate(input, schema); //error: ValidationError: "isA" missing required peer "AVal"
提示 AVal 必填
再來:
var input = { username: 'zzbo', isA: true, AVal: 666666 } var output = Joi.validate(input, schema); //error: null
成功通過校驗
這種極簡易讀的表達方式讓你輕鬆快速實現校驗功能,不需要 if else。這就是配置大於編碼的魅力。
不僅於scheme對象
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
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