在编写api的时候通常都需要对参数进行校验,包括:
- 参数的类型、必填等;
- 字符串,是否可以为空、该符合什么规则等;
- 数字,最大值最小值是什么等等等等。
Joi 是 hapijs 自带的数据校验模块,它已经高度封装常用的校验功能。
1.安装及使用:
//安装
npm install joi --save
//引用
import Joi from 'joi'
2.基础使用
使用joi进行校验,首先要定义它的校验规则,也叫schema。
const schema = Joi.string()
上面就定义了一个校验字符串类型的规则,这个schema会有一个 validate方法,传入需要校验的值:
validate方法会返回一个对象,如果验证通过,就只会返回value属性,如果验证错误,就还有一个error对象,其中error对象的message描述了失败原因:
const schema = Joi.string()
const result = schema.validate(1)
console.log(result)
// result:
{
value: 1,
error: [Error [ValidationError]: "value" must be a string] {
_original: 1,
details: [ [Object] ]
}
}
因此,在此次介绍中,我将验证结果进行了封装,且下文将延续使用
/**
* joi数据校验
* @param schema joi校验schema
* @param data 需要验证的数据
*/
function joiVerification(schema, data) {
const {error, value} = schema.validate(data);
if (error) throw new Error(JSON.stringify({code: 4003, message: `入参信息有误: ${error.message}`}));
return value;
}
3.schema基本校验方法
3.1 数据类型校验
.string()
检验字符串类型
let stringSchema = Joi.string();
joiVerification(stringSchema, 1);
.number()
检验数字类型
let Schema = Joi.number();
joiVerification(Schema, 1);
.array()
检验数组类型
let Schema = Joi.array();
joiVerification(Schema, []);
.boolean()
检验布尔类型
let Schema = Joi.boolean();
joiVerification(Schema, true);
.object()
检验对象类型
let Schema = Joi.object();
joiVerification(Schema, {});
.date()
检验时间类型
let Schema = Joi.date();
joiVerification(Schema, new Date("2022-01-01"));
3.2 常用schema校验
.required()
必选属性,如果需要此校验字段,需要将这个校验规则放到最后面。
let Schema = Joi.object({
name:Joi.string().required()
});
joiVerification(Schema, {name:"long"});
.pattern()
正则校验
let Schema =Joi.string().pattern(/\d/ig);
.optional()
可选字段,默认所有的字段都是可选的,
但是这个校验允许值为undefined但是不允许为null;
可以于.allow()一起使用
let info = {
name:"long",
age:null,
sex:null
};
let Schema = Joi.object({
name: Joi.string().required(),
age: Joi.string().optional(),
sex: Joi.string().optional().allow(null),
});
joiVerification(Schema, info);
//输出:"入参信息有误: \"sex\" must be a string"
let info = {
name:"long",
age:undefined,
sex:null
};
let Schema = Joi.object({
name: Joi.string().required(),
age: Joi.string().optional(),
sex: Joi.string().optional().allow(null),
});
joiVerification(Schema, info);
//输出:成功通过校验
.allow(…values)
在校验条件外允许某些值通过
- …values:一个或多个值
let Schema = Joi.object({
sex: Joi.string().optional().allow(null,"1234"),
});
joiVerification(Schema, info);
.invalid(…values)/别名:disallow,not
不允许以下值存在
- values:可以是任何类型的禁止值,在应用任何其他规则之前将与验证值匹配.
- 如果第一个值为Joi.override, 将覆盖任何先前设置的值.
let info = {
sex:"100"
};
let Schema = Joi.object({
sex: Joi.string().invalid("100").required(),
});
joiVerification(Schema, info);
//输出:"入参信息有误: \"sex\" contains an invalid value"
.override()
any.allow()与、any.invalid()和一起使用的特殊值,any.valid()作为重置任何先前设置的值的第一个值。
个人认为,这个校验并没什么用;
Joi.valid(1).valid(Joi.override, 2);
// Same as:
Joi.valid(2);
// Whereas:
Joi.valid(1).valid(2);
// Is the same as:
Joi.valid(1, 2);
.default([value])
默认值
- 【value】:只允许有一个值,但是取值允许为
- 字符串、数字、对象等
- 使用签名返回默认值的函数,function(parent, helpers)其中:
- parent- 包含正在验证的值的对象的克隆。请注意,由于指定parent参数会执行克隆,因此如果您不使用格式参数,请不要声明它们。
- helpers- 与 中描述的相同any.custom()。
let Schema = Joi.object({
sex: Joi.string().optional().default("1").allow(null,"1234"),
});
joiVerification(Schema, info);
//下面是官方的函数例子,
const generateUsername = (parent, helpers) => {
return parent.firstname.toLowerCase() + '-' + parent.lastname.toLowerCase();
};
generateUsername.description = 'generated username';
const schema = Joi.object({
username: Joi.string().default(generateUsername),
firstname: Joi.string(),
lastname: Joi.string(),
created: Joi.date().default(Date.now),
status: Joi.string().default('registered')
});
.valid(…values)/别名:equal
允许的枚举值
let info = {
sex:"1"
};
let Schema = Joi.object({
sex: Joi.string().valid("0","1").required(),
});
joiVerification(Schema, info);
//同样效果,逻辑相同,不要问为什么不能传array了,运算符...了解一下
let info = {
sex:"1"
};
let sexEnum = ["0","1"];
let Schema = Joi.object({
sex: Joi.string().valid(...sexEnum).required(),
});
joiVerification(Schema, info);
.when([condition],options)
官方解释:添加在验证期间评估的条件并在将架构应用于值之前对其进行修改
其实就是:相当于条件判断
- condition:属性名称
- options:对象属性,判断条件及处理方案
- is- 表示为joi模式的条件。任何不是joi模式的东西都将使用Joi.compile进行转换。默认情况下,is条件架构允许undefined值。用于.required()覆盖。例如,用于is: Joi.number().required()保证joi引用存在并且是一个数字。
- not- 的否定版本is(then并且otherwise具有相反的角色)。
- then- 如果条件为真,则要使用的joi模式。
- otherwise- 如果条件为假,则使用joi模式。
- switch-{ is, then }根据condition. 数组中的最后一项也可能包含otherwise.
- break- 如果规则导致匹配then,otherwise则停止处理所有其他条件。switch
一般情况,options包含is、then、otherwise即可,甚至,otherwise都可以不要
//如果email存在时,判断email是否符合邮箱格式,符合,则判断name字段必填,都则,name属性可以选填
let info = {
email: "test@163.com",
name:"long"
};
let Schema = Joi.object({
email: Joi.string(),
name: Joi.string().when("email",{is:Joi.string().email(),then:Joi.string().required()})
});
joiVerification(Schema, info);
3.3 joi封装的实用校验规则
.min(limit)
指定数组中的最小项目数、数字最小值、字符串最小长度
- limit- 允许的最小数组项数或引用。
let info = {
email: "test@163.com",
age:16,
address:["郑州","衢州","广州"]
};
let Schema = Joi.object({
email: Joi.string().min(1),
age: Joi.number().min(15),
address:Joi.number().min(2)
});
joiVerification(Schema, info);
.max(limit)
指定数组中的最大项目数、数字最大值、字符串最大长度
- limit- 允许的最小数组项数或引用。
let info = {
email: "test@163.com",
age:16,
address:["郑州","衢州","广州"]
};
let Schema = Joi.object({
email: Joi.string().min(1).max(18),
age: Joi.number().min(15).max(100),
address:Joi.number().min(2).max(15)
});
joiVerification(Schema, info);
.length(limit,[encoding])
指定所需的确切字符串长度
- limit- 所需的字符串长度或参考。
- encoding- 如果指定,则使用提供的编码以字节为单位计算字符串长度。
let info = {
mobile: "13027711111"
};
let Schema = Joi.object({
mobile: Joi.string().length(11)
});
joiVerification(Schema, info);
.email([oprions])
要求字符串值是有效的电子邮件地址,一般options不用传入任何信息
- options- 可选设置:
- allowFullyQualified- 如果,允许以字符true结尾的域。.默认为false.
- allowUnicode- 如果true,则允许使用 Unicode 字符。默认为true.
- ignoreLength- 如果true,忽略无效的电子邮件长度错误。默认为false.
- minDomainSegments- 域所需的段数。默认设置不包括单个分段域,例如example@io哪个是有效的电子邮件但非常不常见。默认为2.
- maxDomainSegments- 允许的域段的最大数量。默认为无限制。
- multiple- 如果true, 允许在单个字符串中使用多个电子邮件地址,由, 或separator字符分隔。默认为false.
- separator- 当multipleis时true,覆盖默认,分隔符。字符串可以是单个字符或多个分隔符。默认为’,'.
- tlds- TLD(顶级域)验证选项。默认情况下,TLD 必须是IANA 注册表中列出的有效名称。要禁用验证,请设置tlds为false。要自定义 TLD 的验证方式,请设置以下选项之一:
- allow- 之一:
- true使用已注册 TLD 的 IANA 列表。这是默认值。
- false允许deny列表中未列出的任何 TLD(如果存在)。
- 允许的 TLD的一个Set或数组。不能与 一起使用deny。
- deny- 之一:
- 一个Set或一组被禁止的 TLD。不能与自定义allow 列表一起使用。
- allow- 之一:
let info = {
email: "test@163.com"
};
let Schema = Joi.object({
email: Joi.string().email()
});
joiVerification(Schema, info);
.uri([options])
校验uri是否符合规范
- options- 可选设置:
- scheme- 指定一个或多个可接受的方案,应该只包括方案名称。可以是数组或字符串(字符串会自动转义以在正则表达式中使用)。
- allowRelative- 允许相对 URI。默认为false.
- relativeOnly- 仅限制相对 URI。默认为false.
- allowQuerySquareBrackets- 允许在查询字符串中使用未编码的方括号。这不符合 RFC 3986 标准,但现在查询字符串abc[]=123&abc[]=456非常普遍。默认为false.
- domain- 使用中指定的选项验证域组件string.domain()。
let info = {
callbackUri: "https://www.baidu.com"
};
let Schema = Joi.object({
callbackUri: Joi.string().uri({scheme:["https"]})
});
joiVerification(Schema, info);
truncate([enabled])
指定是否string.max()应将限制用作截断
- enabled- 可选参数默认true允许您通过提供虚假值来重置截断的行为。
let info = {
name: "long1234"
};
let Schema = Joi.object({
name: Joi.string().max(4).truncate()
});
let value = joiVerification(Schema, info);
console.log(value);
//输出:long