前情回顾 在checker v1 的文章 中,讲到了:
原生的参数校验方法冗长
使用tag的校验方法可读性差,容易出错,性能不高(需要使用反射读取每一个字段的标签,解析标签内容,生成校验逻辑),与结构体强耦合
checker 的校验方法清晰简洁,与结构体解耦。
回顾之前的例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 type Req struct { Info typeInfo Email string } type typeStr string type typeInfo struct { Type typeStr Range []string Unit string Granularity string }
checker的校验可以写成下面的代码,只需15行代码就可以把复杂的校验逻辑清楚表示。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 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
变量可以改写成:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 package mainimport ( "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) }
程序输出的错误日志详细,用于定位出错的字段。
1 errMsg :[RangeInt]:Inner.Array[1 ][2 ] should be between 1 and 5 ,actual is 6
自定义的错误提示 将前情回顾中例子的rule
改成
1 2 3 4 5 6 7 8 9 10 11 12 13 rule := checker.And( checker.Email("Email" ).Prompt("Wrong email format" ) checker.And( checker.EqStr("Info.Type" , "range" ), checker.Length("Info.Range" , 2 , 2 ).Prompt("Range's length should be 2" ) checker.Array("Info.Range" , checker.Time("" , "2006-01-02" )). Prompt("Range's element should be time format" ) ), ) validator := checker.NewChecker() validator.Add(rule, "wrong parameter" ) isValid, prompt, _ := validator.Check(item)
Rule
的Prompt
方法用来自定义错误提示。当规则没有通过时,会优先返回规则自己的prompt(代码的[1]/[2]/[3]),如果规则没有自己的prompt, 就会返回添加规则时的prompt(代码中的[4])。
字段缓存 从上面的规则树图示,可以看到,如果具有相同的字段表达式的叶子节点需要被多次校验, 可以将这个叶子节点的表达式的值缓存下来,减少反射调用的开销。
我的公众号:lyp分享的地方
我的知乎专栏: https://zhuanlan.zhihu.com/c_1275466546035740672
我的博客:www.liangyaopei.com
Github Page: https://liangyaopei.github.io/