前情回顾
在checker v1的文章中,讲到了:
- 原生的参数校验方法冗长
- 使用tag的校验方法可读性差,容易出错,性能不高(需要使用反射读取每一个字段的标签,解析标签内容,生成校验逻辑),与结构体强耦合
- checker的校验方法清晰简洁,与结构体解耦。
回顾之前的例子:
// Req.Email需要符合电子邮箱的格式
type Req struct {
Info typeInfo
Email string
}
type typeStr string
// Req.Info.Type = "range",typeInfo.Type的长度为2,元素都是格式符合"2006-01-02"
// Req.Info.Type = "last",typeInfo.Type的长度为1,元素是正整数,Granularity只能是day/week/month之一
type typeInfo struct {
Type typeStr
Range []string
Unit string
Granularity string
}
checker的校验可以写成下面的代码,只需15行代码就可以把复杂的校验逻辑清楚表示。
rule := And(
Email("Email"),
Field("Info",
Or(
And(
EqStr("Type", "range"),
Length("Range", 2, 2),
Array("Range", Time("", "2006-01-02")),
),
And(
EqStr("Type", "last"),
InStr("Granularity", "day", "week", "month"),
Number("Unit"),
),
),
),
)
这里的rule变量,构成了一个规则树。
规则树的构成灵活,上面的rule变量可以改写成:
rule := And(
Email("Email"),
Or(
And(
EqStr("Info.Type", "range"),
Length("Info.Range", 2, 2),
Array("Info.Range", Time("", "2006-01-02")),
),
And(
EqStr("Info.Type", "last"),
InStr("Info.Granularity", "day", "week", "month"),
Number("Info.Unit"),
),
),
)
构成了另一个规则树:
尽管规则树不一样,但是树的叶子节点的fieldExpr是一样的(这可以缓存字段),校验逻辑也是一样的。
v2的更新
更新分为三部分:
- 详细的错误日志,定位出错字段
- 自定义的错误提示
- 字段缓存
错误日志
下面的列子,要校验结构体的二维数组的值,范围在[1,5]
package main
import (
"fmt"
"github.com/liangyaopei/checker"
)
func main() {
type ArrayStruct struct {
Inner struct {
Array [][]int
}
}
arr := [][]int{
{1, 2, 3},
{4, 5, 6},
{7, 8, 9},
}
st := ArrayStruct{}
st.Inner.Array = arr
rule := checker.Array("Inner.Array", checker.Array(
"", checker.RangeInt("", 1, 5)))
validator := checker.NewChecker()
validator.Add(rule, "worng ArrayStruct")
_, _, errMsg := validator.Check(st)
fmt.Printf("errMsg:%v", errMsg)
}
程序输出的错误日志详细,用于定位出错的字段。
errMsg:[RangeInt]:Inner.Array[1][2] should be between 1 and 5,actual is 6
自定义的错误提示
将前情回顾中例子的rule改成
rule := checker.And(
checker.Email("Email").Prompt("Wrong email format") // [1],
checker.And(
checker.EqStr("Info.Type", "range"),
checker.Length("Info.Range", 2, 2).Prompt("Range's length should be 2") // [2],
checker.Array("Info.Range", checker.Time("", "2006-01-02")).
Prompt("Range's element should be time format") // [3],
),
)
validator := checker.NewChecker()
validator.Add(rule, "wrong parameter") // [4]
isValid, prompt, _ := validator.Check(item)
Rule的Prompt方法用来自定义错误提示。当规则没有通过时,会优先返回规则自己的prompt(代码的[1]/[2]/[3]),如果规则没有自己的prompt, 就会返回添加规则时的prompt(代码中的[4])。
字段缓存
从上面的规则树图示,可以看到,如果具有相同的字段表达式的叶子节点需要被多次校验, 可以将这个叶子节点的表达式的值缓存下来,减少反射调用的开销。
我的公众号:lyp分享的地方
我的知乎专栏: zhuanlan.zhihu.com/c_127546654…
我的博客:www.liangyaopei.com
Github Page: liangyaopei.github.io/