js-param-checker v1.1.9
参数校验组件
前言
如果我们每个接口的开头都充斥着上十行参数校逻辑验的代码,想必这一定是一份非常不友好的代码。
而本组件的产生,就是为了解决这类问题,使用本组件,可以屏蔽大部分关于参数校验的逻辑,让接口的代码更加专注于处理业务逻辑,同时也能提高代码的可读性。
安装方式
$ npm install js-param-checker
快速上手
为了让读者更快了解本组件,我们举一个简单的例子来说明如何使用本组件
读者可以拷贝代码到本地运行看效果
const paramChecker = require('js-param-checker'); // 引入本组件
const checkInfo = { // 将参数的校验放到一个配置对象里面
"productName": "require string(0,)", // 必填,且为字符串,字符串长度大于0
"productPrice": "require int(0,)", // 必填,且为整数,整数大于0
"productCount": "require uintz", // 必填,且为非负整数
"productFrom": "require enums(中国,美国,加拿大)" // 必填,且必须是中国、美国、加拿大其中之一
};
const doBusiness = (dataInfo) => { // 假定这个方法用于处理业务逻辑
// 按照通常情况下,我们可能会在前面这里对每个参数进行校验
// 使用本组件,我们只需要短短的几行代码,甚至可以把它提取到公共是外部框架中去做处理
const error = paramChecker.check(dataInfo, checkInfo);
//如果校验格式不正确,error为一个错误对象,error.msg为错误文本提示,否则为null
if(error){
throw new Error(error.msg);
}
console.log('dataInfo check ok !');
// 业务逻辑
}
const dataInfo = {
productName: "苹果",
productPrice: 5000,
productCount: 1,
productFrom: "英国" // 这里会通不过,因为不在枚举的三个值中
};
doBusiness(dataInfo); // 这里我们调用该方法执行业务逻辑
上面的例子中,我们把参数校验通过调用 paramChecker.check 来处理,在实际项目中,我们可以把 checkInfo 写成一个配置文件,所有的校验文件统一放到专门的目录下统一管理,甚至连校验那一步都可以进一步封装,即业务方法里面不需要出现任何参数校验的代码,并且参数校验又是可配置的。
功能概览
- 内置多种常见的格式校验类型
- 支持必填和非必填
- 支持校验任何复杂的json对象格式数据
- 支持用户添加自定义校验格式
- 支持用户修改默认的校验名称
- 支持用户重写默认的校验格式
- 支持对校验的格式进行扩展
- 支持异步方式校验
- 支持错误提示参数命名
功能详解
组件内置支持的校验格式如下
string ~字符串(可设置长度)
enums ~枚举
variable ~js变量
letter ~字母
upletter ~大写字母
lowletter ~小写字母
timestamp ~时间戳
time ~时间
date ~日期
datetime ~日期时间
email ~邮件
phone ~手机号码
ip ~IP地址
url ~url链接
postcode ~邮编号码
idcard ~身份证号
notchinese ~非中文
chinese ~中文
bool ~布尔
int ~整数(可设置取值范围)
uint ~正整数(可设置取值范围)
uintz ~自然数(可设置取值范围)
float ~浮点数
ufloat ~正浮点数
array ~数组
uarray ~非空数组
object ~对象
uobject ~非空对象
支持参数的必填和非必填校验
组件通过关键字 require 和 optional 来声明参数是否必填,如
{
"createDate": "require date", //创建时间必填
"remark": "optional string" //备注可不填
}
上面例子中,createDate参数是必填的,且约定为date格式,如果检验结果为没值或格式不对,就会提示 "createDate必填,且为YYYY-MM-DD的日期格式",而remark参数如果没值则不会有错误提示
组件支持对任意复杂的json格式对象进行校验
有时候我们的参数并不是形如
{
product: "apple",
count: 120
}
这种简单的对象,而是各种数组和对象嵌套,如
{
product: {
name:"apple",
sale: {
byMonth: [20, 10, 11, 12, 20, ...], //数组每一项表示每个月的销售额
bySaleman: { //每个销售员的销售额
lilei: 20,
lily: 30,
...
}
},
...
}
}
而本组件就是针对这种场景,可支持对数组内的参数进行校验,甚至可以精确到校验数组的某一项,或者是更深层次的值校验(文章后面会有例子)。
支持用户添加自定义校验格式
有时候用户需要有自己的一些自定义校验规则,本组件支持用户添加自己的校验规则,方式如下:
paramChecker.setting({
add: {
myrule: (name, value) => {
if(typeof value !== 'object'){
return `${name}不符合自定义校验规则`;
}else{
return null;
}
}
}
});
在使用的时候,就可以通过如
{
"createDate": "require date", // 创建时间必填
"remark": "optional string", // 备注可不填
"moreInfo": "require myrule" // 组件会调用上面设置的自定义规则来校验
}
如果我们传入的参数值如下
{
"createDate": "2019-05-10",
"remark": "测试",
"moreInfo": "test"
}
那么校验结果会是 "moreInfo不符合自定义校验规则",上面完整的例子如下:
const paramChecker = require('js-param-checker');
paramChecker.setting({
add: {
myrule: (name, value) => {
if(typeof value !== 'object'){
return `${name}不符合自定义校验规则`;
}else{
return null;
}
}
}
});
const error = paramChecker.check(
{
"createDate": "2019-05-10",
"remark": "测试",
"moreInfo": "test"
},
{
"createDate": "require date", // 创建时间必填
"remark": "optional string", // 备注可不填
"moreInfo": "require myrule" // 组件会调用上面设置的自定义规则来校验
}
);
console.log(`校验结果:${error?error.msg:'ok'}`); // 校验结果:moreInfo不符合自定义校验规则
上面如果要校验通过,moreInfo需要是一个json对象,如
{
"createDate": "2019-05-10",
"remark": "测试",
"moreInfo": {
"color": "red",
"price": 100
}
}
此时会输出 "校验结果:ok"
支持用户修改默认的校验名称
这个功能是为了能让用户可以修改本组件内置的格式名称,比如你可以把内置规则名"date"改名为"DATE","string"改为"str",那么上面的例子中,需要修改如下
paramChecker.setting({ //说明:paramChecker.setting是可以多次调用的,每次调用的结果会叠加
rename: {
string: 'str'
}
});
{
"createDate": "require date", // 创建时间必填
"remark": "optional str", // 备注可不填
"moreInfo": "require myrule" // 组件会调用上面设置的自定义规则来校验
}
支持用户重写默认的校验格式
有时候内置的校验格式并不适应实际的应用场景,比如内置的date的校验格式是YYYY-MM-DD,而实际是需要YYYY/MM/DD的格式,那么可以重写内置的校验格式,方式如下:
paramChecker.setting({
override: {
date: (name, value) => {
const reg = /^(\d{4})\/(\d{2})\/(\d{2})$/;
const matchs = value.match(reg);
if(matchs === null || matchs.length === 0){
return `${name}必须为YYYY/MM/DD格式,当前值:${value}`;;
}
return null;
}
}
});
支持对校验的格式进行扩展
上面的myrule自定义格式中,如果传入的不是json对象,会提示不符合自定义格式。而如果传入的是对象,则能校验通过。如果希望对这个规则进行扩展,比如校验对象的属性个数,那么可以使用如下方式扩展
paramChecker.setting({
add: {
myrule: (name, value, extra) => {//这里多出来的extra参数,就是用于扩展规则校验的,后面会说明如何传入这个参数
if(typeof value !== 'object'){ //这里有点不严谨,数组也是对象,但是作为例子,大家知道意思就行
return `${name}不符合自定义校验规则`;
}else{
if(Object.keys(value).length !== parseInt(extra)){
return `${name}不符合自定义校验规则`;
}else{
return null;
}
}
}
}
});
接下来我们来约定,moreInfo必须是一个拥有三个属性的对象
const error = paramChecker.check(
{
"createDate": "2019-05-10",
"remark": "测试",
"moreInfo": {
"color": "red",
"price": 100
}
},
{
"createDate": "require date", // 创建时间必填
"remark": "optional string", // 备注可不填
"moreInfo": "require myrule(3)" // 这里通过在myrule后面加上小括号,小括号里面的值就是传入自定义规则的实现方法的extra参数
}
);
console.log(error); //这里会打印出一个错误对象 {"msg":"moreInfo不符合自定义校验规则", "param":"moreInfo", "check":"require myrule(3)", "value":{"color":"red","price":100}}
支持异步方式校验
有时候参数是否符合要求需要调用后台接口去判断,比如有时候我们拿到的城市参数在db中是动态变化的,此时就需要异步去db获取这个动态的枚举值,本组件可通过异步的方式来校验参数值。
异步校验同样支持新增校验规则和重写原规则,新增规则的例子如下
const paramChecker = require('js-param-checker');
//fetchData模拟去后台获取城市的枚举值
const fetchData = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(['深圳', '广州', '上海'])
}, 100);
});
}
paramChecker.setting({
add: {
myrule: async (name, value) => { // 这里需要使用到async声明这是一个异步方法
const data = await fetchData();
if(data.indexOf(value)>=0){ // 判断value是否属于后台返回的数据中的一个
return null;
}else{
return `${name}不符合自定义校验规则`;
}
}
}
});
//接下来在使用方面也会稍有不同,异步方式需要调用 checkAsync 方法
paramChecker.checkAsync({
"createDate": "2019-05-10",
"remark": "测试",
"moreInfo": "北京"
},
{
"createDate": "require date", // 创建时间必填
"remark": "optional string", // 备注可不填
"moreInfo": "require myrule" // 这里会去后台db拉去数据校验
}
).then((error) => {
console.log(`校验结果:${error?error.msg:'ok'}`);
});
支持错误提示参数命名
有时候我们希望给到用户的是中文或者更加通熟易懂的名称,上面的例子中,
"createDate": "require date", // 创建时间必填
如果 createDate 格式错误
"createDate": "20190510",
那么提示会如下
createDate必须为YYYY-MM-DD格式,当前值:20190510
我们可以设置如下
"createDate 产品创建时间": "require date", // 创建时间必填
那么提示会变成: "产品创建时间必须为YYYY-MM-DD格式,当前值:20190510"
以上这种提示方式是不是就变得更加友好呢?
另外,我们重命名的参数在规则方法中可以通过name参数取到,如
myrule: (name, value) => { // 这里的name就会变成 产品创建时间
}
复杂示例
// 下面直接模拟一个比较复杂的场景,假设我们需要校验一个userInfo对象的格式,这个对象嵌套了复杂的数据结构 // 读者可以把以下的例子拷贝到本地测试,修改字段的值,看看校验的提示
const paramChecker = require('js-param-checker');
const userInfo = {
name: 'zzz',
age: 18,
sex: 'male',
nation: '汉',
description: '一枚好青年',
birthday: '2020-01-01',
contact: [
13888888888,
'zhen_zhen_zhang@163.com',
{
qq: ['88888', '999999']
},
{
wechat: 'iloveyou'
}
],
company: {
name: 'tencent',
city: '深圳',
homepage: 'https://www.tencent.com',
groups: [
{
groupName: 'WXG',
staffCount: 1,
},
{
groupName: 'IEG',
staffCount: 2,
},
{
groupName: 'CDG',
staffCount: 3,
}
],
products: [
{
productName: 'qq',
productInfo: {
userCount: 4,
publishTime: '2019-01-01'
},
},
{
productName: 'wechat',
productInfo: {
userCount: 5,
publishTime: '2020-01-01'
},
}
]
}
};
const checkInfo = {
"name": "require string(0,)", // 名称长度必须大于0
"age": "require uint", // 年龄必须大于0
"sex": "require enums(female,male)", // 性别必须为female和male其中之一
"nation 民族": "require chinese", // 民族必须为汉字
"description": "require string(0,)", // 个人描述长度必须大于0
"birthday": "require date", // 生日格式必须为YYYY-MM-DD
"contact": "require uarray", // 联系方式是个非空数组
"contact[0]": "require phone", // 联系方式第一项必须为手机号码
"contact[1]": "require email", // 联系方式第二项必须为邮箱
"contact[2]": "require uobject", // 联系方式第三项必须为非空objecj对象
"contact[2].qq": "require array", // 联系方式第三项的属性包含qq,且qq对应的值是数组(数组可为空)
"contact[2].qq[]": "require string(4,)", // 联系方式qq的位数长度必须大于4
"contact[3]": "require uobject", // 联系方式第四项必须为非空boject对象
"contact[3].wechat": "require string(0,)", // 联系方式第四项的属性必须包含wechat,且wechat的长度必须大于0
"company": "require uobject", // 公司必须是非空的object对象
"company.name": "require string(0,)", // 公司名称长度必须大于0
"company.city": "require enums(深圳,广州,北京,上海)", // 公司城市只能是 “深圳,广州,北京,上海” 中的一个
"company.homepage": 'require url', // 公司主页
"company.groups": "require uarray", // 公司的事业群是一个非空数组
"company.groups[]": "require uobject", // 每个事业群是一个非空对象
"company.groups[].groupName": "require upletter", // 事业群名称必须是大写字母组成
"company.groups[].staffCount": "require uint", //事业群人数必须是正整数
"company.products": "require uarray", // 公司生产的产品是一个非空数组
"company.products[]": "require uobject", // 每个产品是一个非空对象
"company.products[].productName": "require string(0,)", // 产品名称长度必须大于0
"company.products[].productInfo": "require uobject", // 产品详情是一个非空对象
"company.products[].productInfo.userCount": "require uint", // 产品详情的用户数必须是正整数
"company.products[].productInfo.publishTime": "require date" // 产品详情的发布时间必须是YYYY-MM-DD格式
};
const error = paramChecker.check(userInfo, checkInfo);
console.log(`result: ${error?error.msg:'ok'}`);
其他依赖
无