Thrift Validator
概述
Validator 是用于支持结构体校验能力的 thriftgo 插件。
在 IDL 中通过注解来描述约束,插件会根据注解给对应的 struct 生成 IsValid() error
方法,生成在 xxx-validator.go 文件。
注解采用 vt.{ConstraintType} = "Value"
这种形式描述。
适用范围:struct/union 中的每个 field 。
校验形式:用户主动校验。(可提供中间件,统一对所有参数/结果校验)
IDL 示例:
enum MapKey {
A, B, C, D, E, F
}
struct Request {
1: required string Message (vt.max_size = "30", vt.prefix = "Debug")
2: i32 ID (vt.gt = "1000", vt.lt = "10000")
3: list<double> Values (vt.elem.gt = "0.25")
4: map<MapKey, binary> KeyValues (vt.key.defined_only = "true", vt.min_size = "1")
}
struct Response {
1: required string Message (vt.in = "Debug", vt.in = "Info", vt.in = "Warn", vt.in = "Error")
}
安装
使用 Validator 插件前需要先进行安装,才可以使用,否则会报找不到 thrift-gen-validator
可执行文件错误( exec: "thrift-gen-validator": executable file not found in $PATH
)。
如果你已经安装好 Golang 和 Kitex 命令行工具,请执行如下命令安装 thrift-gen-validator
插件:
$ go install github.com/cloudwego/thrift-gen-validator@latest
执行完 go install
之后,会将编译后的 thrift-gen-validator
二进制文件安装到 $GOPATH/bin
下。
可以执行下面的命令,验证是否安装成功。
$ cd $(go env GOPATH)/bin
$ ls
go1.20.1 goimports hz thrift-gen-validator
godotenv golangci-lint kitex thriftgo
$ cd ~/ && thrift-gen-validator --help
Usage of thrift-gen-validator:
-version
Show the version of thrift-gen-validator
(0x1232358,0x1370f70)
cd ~/
是为了验证在任意目录下都可以愉快地调用 thrift-gen-validator
。如果在执行该命令时出现类似上述找不到 thrift-gen-validator
可执行文件错误,请检查 $GOPATH
是否被正确设置到 $PATH
中。
关于 thrift-gen-validator
的安装及其他更多信息,可参阅 thrift-gen-validator
使用
以快速开始里的 Kitex Hello 项目为例,进入示例仓库的 hello
目录,在 hello.thrift
中添加注解,例如我们对 Request
结构体的 message
字段进行约束,约束长度不超过8且要以 “kitex-” 前缀开头:
struct Request {
1: string message (vt.max_size = "8", vt.prefix = "kitex-")
}
在生成 Kitex 代码时,加上 --thrift-plugin validator
参数,即可生成 validator 文件。
kitex --thrift-plugin validator -service a.b.c hello.thrift
执行后,可以看见新生成的 Validator:
├── kitex_gen
└── api
├── hello
│ ├── client.go
│ ├── hello.go
│ ├── invoker.go
│ └── server.go
├── hello.go
--> ├── hello_validator.go
├── k-consts.go
└── k-hello.go
其中对于 Request
结构体,新生成了 IsValid()
方法:
func (p *Request) IsValid() error {
if len(p.Message) > int(8) {
return fmt.Errorf("field Message max_len rule failed, current value: %d", len(p.Message))
}
_src := "kitex-"
if !strings.HasPrefix(p.Message, _src) {
return fmt.Errorf("field Message prefix rule failed, current value: %v", p.Message)
}
return nil
}
在后续的使用中,调用 IsValid()
方法对结构体进行校验即可:
req := &api.Request {
//....
}
err := req.IsValid()
if err != nil {
//invalid ....
}
//valid ...
支持的校验能力
校验顺序以定义顺序为准, ‘in’ 和 ‘not_in’ 这类可以定义多次的,以第一次出现的顺序为准。
数字类型
包括 i8,i16,i32,i64,double。
- const,必须为指定值。
- lt,le,gt,ge,分别表示小于,小于等于,大于,大于等于。
- in,not_in,分别表示可以使用的值和不可以使用的值,可多次指定,一次指定一个值。
- not_nil,该字段不能为空。(仅当字段为 optional 时合法)
struct NumericDemo {
1: double Value (vt.gt = "1000.1", vt.lt = "10000.1")
2: i8 Type (vt.in = "1", vt.in = "2", vt.in = "4")
3: i64 MagicNumber (vt.const = "0x5f3759df")
}
string/binary
- const,必须为指定值。
- min_size,max_size,最大长度,最小长度。
- pattern,正则匹配。
- prefix,suffix,contains,not_contains,限制前缀,限制后缀,必须包含,不能包含。
- in,not_in,分别表示可以使用的值和不可以使用的值,二者不能同时使用,可多次指定,一次指定一个值。
- not_nil,该字段不能为空。(仅当字段为 optional 时合法)
struct StringDemo {
1: string Uninitialized (vt.const = "烫烫烫")
2: string Name (vt.min_size = "6", vt.max_size = "12")
3: string SomeStuffs (vt.pattern = "[0-9A-Za-z]+")
4: string DebugInfo (vt.prefix = "[Debug]")
5: string PanicInfo (vt.contains = "panic")
6: string Editor (vt.in = "vscode", vt.in = "vim", vt.in = "goland")
}
bool
- const,必须为指定值。
- not_nil,该字段不能为空。(仅当字段为 optional 时合法)
struct BoolDemo {
1: bool AMD (vt.const = "true")
2: optional bool Nvidia (vt.not_nil = "false")
}
enum
- const,必须为指定值。
- defined_only,必须在 enum 中定义的值中。
- not_nil,该字段不能为空。(仅当字段为 optional 时合法)
enum Type {
Number, String, List, Map
}
struct EnumDemo {
1: Type AddressType (vt.const = "String")
2: Type ValueType (vt.defined_only = "true")
3: optional Type OptType (vt.not_nil = "true")
}
set/list
- min_size,max_size,最小长度,最大长度。
- elem,元素约束。
struct SetListDemo {
1: list<string> Persons (vt.min_size = "5", vt.max_size = "10")
2: set<double> HealthPoints (vt.elem.gt = "0")
}
map
- min_size,max_size,最小键值对数,最大键值对数。
- no_sparse,value 为指针时,不能为 nil 。
- key,value,键约束,值约束。
struct MapDemo {
1: map<i32, string> IdName (vt.min_size = "5", vt.max_size = "10")
2: map<i32, DemoTestRequest> Requests (vt.no_sparse = "true")
3: map<i32, double> Some, (vt.key.gt = "0", vt.value.lt = "1000")
}
struct/union/exception
- skip,跳过该 struct/union/exception 的递归校验。(作为单独字段时默认为 false,作为元素时默认为 true )
- not_nil,该字段不能为空。
struct OuterRequest {
1: SomeStruct Struct (vt.skip = "true")
2: SomeUnion Union (vt.skip = "true")
3: SomeStruct NotNilStruct (vt.not_nil = "true")
}
变量引用
前置符 $
表示某个变量的引用,可用于跨字段校验:
$x
代表名为x
的变量,变量名为\[a-zA-Z0-9_]\
,其作用域规则为当前结构体。$
表示 validator 所处的当前字段。
struct Example {
1: string A (vt.max_size = "$C")
2: string B (vt.not_in = "$A")
3: i32 C
}
工具函数
前置符 @
表示内置的工具函数来计算校验值,目前支持的工具函数:
sprintf(fmt, $1, $2...)
用于输出特定字符。len($x)
输出变量大小。(字符串长度、list 元素个数)
struct Example {
1: string A
2: list<string> B (vt.max_size = "@len($D)")
3: map<string,int) C
4: string D (vt.const = "@sprintf(\"%s_%s\", $A, \"mysuffix\")")
}