阅读 344

checker v2 高效灵活的Golang参数校验

前情回顾

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_tree.png

规则树的构成灵活,上面的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"),
			),
		),
	)
复制代码

构成了另一个规则树:

rule_tree2.png

尽管规则树不一样,但是树的叶子节点的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)
复制代码

RulePrompt方法用来自定义错误提示。当规则没有通过时,会优先返回规则自己的prompt(代码的[1]/[2]/[3]),如果规则没有自己的prompt, 就会返回添加规则时的prompt(代码中的[4])。

字段缓存

从上面的规则树图示,可以看到,如果具有相同的字段表达式的叶子节点需要被多次校验, 可以将这个叶子节点的表达式的值缓存下来,减少反射调用的开销。

我的公众号:lyp分享的地方

我的知乎专栏: zhuanlan.zhihu.com/c_127546654…

我的博客:www.liangyaopei.com

Github Page: liangyaopei.github.io/

文章分类
后端
文章标签