Why
社区中有很多数据校验库,各功能不一。有些库有你想要的功能,有些库又没有。有写库又喜欢定义自己的模板,然后这些模板改着改着就成了 Magic String(魔符)
例如这样的模板:
validate(data, 'size:4')
validate(data, 'size:2-4')
validate(data, 'lt:20 || gt: 60 && is_prime')
...
现在是大前端时代,讲究的是 All in Javascript。也符合 React 的哲学。另一个套路 Javascript in HTML(Template) 是不友好的(AngularJs 我用了一年多)
目前比较满意的应该是这一款: superstruct
但是对我而言,它还是不顺手,如何校验多个条件? 如何做无限嵌套,包括数组/对象嵌套。
先上一个结构体,你们感受一下
深度嵌套的对象
// 如何检验这个数据, 虽然实际中,谁这么定义数据结构,会被打死
const data1 = {
name: "axetroy",
age: 18,
country: {
name: "China",
province: {
name: "You Guess",
city: {
name: "The capital of the province",
street: {
name: "suburbs",
number: {
code: 100031,
owner: "Axetroy",
family: [
{ name: "daddy" },
{ name: "mom" },
{ name: "brother" },
{ name: "sister" }
]
}
}
}
}
}
};
所以才写一个自己顺手的东西。
结构体
先来一个简单的类型校验,只有名字和年龄
const { Struct, type } = require("@axetroy/struct");
const struct = Struct({
name: type.string,
age: type.int
});
它还可以这么写, 自己自定义一个 func 作为校验
const struct = Struct({
name: type.string,
age: type.func(function(input) {
return parseInt(input) === input;
})
});
甚至几个条件去校验一个字段, 带着参数
const struct = Struct({
name: type.string,
age: type.int.gte(18).lte(50) // 需要 >=18 && <=50
});
然后你还能定义某个字段的错误信息
const struct = Struct({
name: type.string,
age: type.int.gte(18).lte(50).msg("保险仅限于18-50周岁之间")
});
然后还支持数组
const struct = Struct({
name: type.string,
age: type.int,
friends: [type.string]
});
同样支持数组里面嵌套对象
const struct = Struct({
name: type.string,
age: type.int,
friends: [
{
name: type.string,
age: type.int
}
]
});
还可以自定义函数
const struct = Struct({
name: type.string,
age: type.int,
friends: [
function(element) {
if (typeof element.name !== "string") {
return false;
} else if (typeof element.age !== "number") {
return false;
}
return true;
}
]
});
如果你还想组合多个校验器,还可以这么用
const struct = Struct({
name: type.string,
age: type.int,
friends: [
type.func(function(element) {
if (typeof element.name !== "string") {
return false;
} else if (typeof element.age !== "number") {
return false;
}
return true;
}).isAdult
]
});
内置的校验器基本够平时校验使用
- number
- int
- float
- string
- bool
- any
- odd
- even
- json
- object(object)
- array(type)
- eq(value)
- gt(number)
- gte(number)
- lt(number)
- lte(number)
- bt(minNumber, maxNumber)
- in(array)
- len(int)
- msg(message)
- func(validatorFunc)
如果这些都没有你想要的,那么你还可以自定义
Struct.define("email", function(input) {
return /^[a-z0-9]+([._\\-]*[a-z0-9])*@([a-z0-9]+[-a-z0-9]*[a-z0-9]+.){1,63}[a-z0-9]+$/.test(
input
);
});
// 然后这么使用
const struct = Struct({
name: type.string,
age: type.int,
email: type.email
});
如果想定义一个带参数的校验器
// 发现了吗,校验器名字,跟上面的区别就是多了括号()
Struct.define("prefixWith()", function(prefix) {
return function(input) {
return input.indexOf(prefix) === 0;
};
});
const struct = Struct({
name: type.string.prefixWith("[A]"), // 名字必须是字符串,并且以[A]开头
age: type.int
});
好了,最后回归到我们开头说的,深度嵌套的对象,怎么去解析
const struct = Struct({
name: type.string,
age: type.int,
country: {
name: type.string,
province: {
name: type.string,
city: {
name: type.string,
street: {
name: type.string,
number: {
code: type.int,
owner: type.string,
family: [
{
name: type.string
}
]
}
}
}
}
}
});
但是实际当中,我们可能不会这么定义这么一个庞大的数据结构。
而是分成细化的结构体。
比如一篇文章:
const User = Struct({
name: type.string,
age: type.int
});
const Article = Struct({
title: type.string,
content: type.string,
author: User // 作者嵌套ser
});
在比如一个项目的信息:
const User = Struct({
name: type.string,
age: type.int
});
const Project = Struct({
name: type.string,
contributors: [User] // 数组的元素为User结构体
});
前面都说了定义结构体,那么输出呢? 依旧从最简单的入手
const struct = Struct({
name: type.string,
age: type.int
});
const err = struct.validate(data);
console.log(err);
struct.validate
是执行校验工作。
如果校验过程中,有某个字段,不符合校验器的要求。
那么校验终止,并且返回这个错误。
如果校验成功,那么返回undefined
校验失败返回的错误为自定义错误TypeError
,继承自Error
TypeError
有一下属性:
- validator: 失败的校验器名字
- path: 路径数组。
- value: 校验失败的值
- message: 错误信息, 默认与 detail 同值,除非你使用
.msg(errorMessage)
去修改错误信息 - detail: 默认的错误信息
{ Error
at Object. (/home/axetroy/gpm/github.com/axetroy/struct/src/error.js:19:23)
at Module._compile (module.js:635:30)
at Object.Module._extensions..js (module.js:646:10)
at Module.load (module.js:554:32)
at tryModuleLoad (module.js:497:12)
at Function.Module._load (module.js:489:3)
at Module.require (module.js:579:17)
at require (internal/module.js:11:18)
at Object. (/home/axetroy/gpm/github.com/axetroy/struct/src/type.js:2:19)
at Module._compile (module.js:635:30)
validator: 'int',
path: [ 'author', 'age' ],
value: '18',
detail: 'Can not pass the validator "int" with value "18" in path "age"',
message: 'Can not pass the validator "int" with value "18" in path "age"' }
最后
总结一下这个库的特点:
- 全部是 Javascript 语法, 没有自定义模板字符串.
- 严格模式. 没有定义类型的字段会不通过
- 支持结构体嵌套
- 可扩展, 可以在行内写校验器, 也可以新增一个全局类型.
- 校验器支持带参数.
- 简明的错误信息输出
- 支持无线嵌套对象/数组. 数组里面嵌套对象, 然后对象里面又嵌套数组. OK.
数据校验库层出不穷,关键也不看是不是很强大。
关键是要自己用得顺手。
代码覆盖率 100%,大量测试通过。
目前已运用到实际的生产环境中。
应用在哪里?
- 配置文件, 程序运行时读取配置文件进行校验
- 测试接口数据的输入/出校验
库没有使用 es6 语法, 也没有使用 Babel 编译, 没有使用 Webpack 打包.
但是理论上是支持浏览器和 Node 的.
浏览器需要使用打包工具引入.
最后上项目地址: struct
大牛们的评论:朕有话说
还没有人评论哦,赶紧抢沙发!