Joi是什么?
官方文档描述是:joi lets you describe your data using a simple, intuitive, and readable language.
简单理解就是:可以简单直接描述你的数据模型的语言。
所以重点是描述,然后校验很简单。
官方文档地址:joi.dev/api/?v=17.4.0
安装
在项目目录里执行 npm i joi
试用环境
@google-cloud/functions-framework
简单试用
对一个字符串型参数进行校验
代码如下:
const Joi = require('joi');
const schema = Joi.object({
username: Joi.string()
.alphanum()
.min(3)
.max(30)
})
exports.helloWorld = (req, res) => {
const { error, value } = schema.validate(req.query)
if (error) {
return res.status(422).json({ error: error })
}
res.send('Hello, World')
}
效果如下:
访问 http://localhost:8080/
返回 Hello, World
访问 http://localhost:8080/?username=aaaa
返回 Hello, World
访问 http://localhost:8080/?username=aa
返回
{
"error": {
"_original": {
"username": "aa"
},
"details": [
{
"message": "\"username\" length must be at least 3 characters long",
"path": [
"username"
],
"type": "string.min",
"context": {
"limit": 3,
"value": "aa",
"label": "username",
"key": "username"
}
}
]
}
}
{
"error": {
"_original": {
"username": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
},
"details": [
{
"message": "\"username\" length must be less than or equal to 30 characters long",
"path": [
"username"
],
"type": "string.max",
"context": {
"limit": 30,
"value": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"label": "username",
"key": "username"
}
}
]
}
}
{
"error": {
"_original": {
"username": "aaaa",
"password": "1234"
},
"details": [
{
"message": "\"password\" is not allowed",
"path": [
"password"
],
"type": "object.unknown",
"context": {
"child": "password",
"label": "password",
"value": "1234",
"key": "password"
}
}
]
}
}
对body进行校验
增加必填校验
代码:
const Joi = require('joi');
const schema = Joi.object({
username: Joi.string()
.alphanum()
.min(3)
.max(30),
password: Joi.string()
.alphanum()
.min(6)
.max(12)
})
exports.helloWorld = (req, res) => {
const { error, value } = schema.validate(req.body)
if (error) {
return res.status(422).json({ error: error })
}
console.info(value)
res.send(value)
};
用postman访问http://localhost:8080
输入数据:
{
"username": "postman",
"password": "123456"
}
返回:
{
"username": "postman",
"password": "123456"
}
输入数据:
{
"username": "postman",
"password": "123456",
"age":30
}
返回:
{
"error": {
"_original": {
"username": "postman",
"password": "123456",
"age": "30"
},
"details": [
{
"message": "\"age\" is not allowed",
"path": [
"age"
],
"type": "object.unknown",
"context": {
"child": "age",
"label": "age",
"value": "30",
"key": "age"
}
}
]
}
}
输入数据:
{
"username": "postman",
"password": ""
}
返回:
{
"error": {
"_original": {
"username": "postman",
"password": ""
},
"details": [
{
"message": "\"password\" is not allowed to be empty",
"path": [
"password"
],
"type": "string.empty",
"context": {
"label": "password",
"value": "",
"key": "password"
}
}
]
}
}
校验两个参数是否都输入
和required是有差别的
代码:
const Joi = require('joi');
const schema = Joi.object({
username: Joi.string()
.alphanum()
.min(3)
.max(30),
password: Joi.string()
.alphanum()
.min(6)
.max(12),
mobile: Joi.string()
.alphanum()
}).and('username', 'password')
exports.helloWorld = (req, res) => {
const { error, value } = schema.validate(req.query)
if (error) {
return res.status(422).json({ error: error })
}
console.info(value)
res.send(value)
};
输入数据:
http://localhost:8080/?username=aaaaaaa&password=123456
返回:
{
"username": "aaaaaaa",
"password": "123456"
}
输入数据:
http://localhost:8080/?username=aaaaaaa&mobile=123456
返回:
{
"error": {
"_original": {
"username": "aaaaaaa",
"mobile": "123456"
},
"details": [
{
"message": "\"value\" contains [username] without its required peers [password]",
"path": [],
"type": "object.and",
"context": {
"present": [
"username"
],
"presentWithLabels": [
"username"
],
"missing": [
"password"
],
"missingWithLabels": [
"password"
],
"label": "value",
"value": {
"username": "aaaaaaa",
"mobile": "123456"
}
}
}
]
}
}
{
"mobile": "123456"
}
二选一及伴随
代码
const Joi = require('joi');
const schema = Joi.object({
username: Joi.string()
.alphanum()
.min(3)
.max(30),
password: Joi.string()
.alphanum()
.min(6)
.max(12),
mobile: Joi.string()
.alphanum()
}).xor('username', 'mobile')
.with('username', 'password')
.with('mobile','password')
exports.helloWorld = (req, res) => {
const { error, value } = schema.validate(req.query)
if (error) {
return res.status(422).json({ error: error })
}
console.info(value)
res.send(value)
};
{
"mobile": "123456789",
"password": "654321"
}
{
"error": {
"_original": {
"mobile": "123456"
},
"details": [
{
"message": "\"mobile\" missing required peer \"password\"",
"path": [],
"type": "object.with",
"context": {
"main": "mobile",
"mainWithLabel": "mobile",
"peer": "password",
"peerWithLabel": "password",
"label": "value",
"value": {
"mobile": "123456"
}
}
}
]
}
}
{
"username": "abcdfe",
"password": "654321"
}
{
"error": {
"_original": {
"username": "abcdfe",
"password": "654321",
"mobile": "13999999999"
},
"details": [
{
"message": "\"value\" contains a conflict between exclusive peers [username, mobile]",
"path": [],
"type": "object.xor",
"context": {
"peers": [
"username",
"mobile"
],
"peersWithLabels": [
"username",
"mobile"
],
"present": [
"username",
"mobile"
],
"presentWithLabels": [
"username",
"mobile"
],
"label": "value",
"value": {
"username": "abcdfe",
"password": "654321",
"mobile": "13999999999"
}
}
}
]
}
}
其他常用功能
直接验证某个变量或者值:Joi.attempt(‘x’, Joi.number());
允许对象里含有未定义的key:Joi.object({ a: Joi.any() }).unknown();
定义参数不能同时出现:
const schema = Joi.object({
a: Joi.any(),
b: Joi.any()
}).nand('a', 'b');