Thrift Validator
Overview
Validator is a thrift plugin that supports struct validation.
Constraints are described by annotations in IDL, and the plugin will generate the IsValid() error
method according to the structure given by the annotation, which is generated in the xxx-validator.go file.
Annotation is described in the form vt.{ContType} = "Value"
.
Scope: Every field in struct/union.
Validation form: User initiative (provided, consistent for all parameters/results).
IDL example:
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")
}
Installation
Before using the Validator plugin, you should install it first.
Otherwise, an error message will be displayed stating that the thrift-gen-validator
executable file cannot be found (exec: "thrift-gen-validator": executable file not found in $PATH
).
If you have already installed Golang and Kitex command-line tools, please run the following command to install the thrift-gen-validator
plugin:
$ go install github.com/cloudwego/thrift-gen-validator@latest
After executing go install
, the compiled thrift-gen-validator
binary file will be installed under $GOPATH/bin
.
You can run the following command to verify that the installation was successful.
$ 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 ~/
command is used to verify that thrift-gen-validator
can be called from any directory.
If an error message similar to the one above appears when executing this command, please check whether $GOPATH
has been correctly set to $PATH
.
For more information on installing and using thrift-gen-validator
, please refer to thirft-gen-validator.
Usage
Take the project “Kitex Hello” in Getting Started as an example, add annotations inhello.thrift
. For example, the code bellow requires the length and prefix for Request.message
:
struct Request {
1: string message (vt.max_size = "8", vt.prefix = "kitex-")
}
When generating kitex code, add --thrift-plugin validator
to generate the validator code.
kitex --thrift-plugin validator -service a.b.c hello.thrift
The code will be generated in the directory bellow:
├── kitex_gen
└── api
├── hello
│ ├── client.go
│ ├── hello.go
│ ├── invoker.go
│ └── server.go
├── hello.go
--> ├── hello_validator.go
├── k-consts.go
└── k-hello.go
For struct Request
, IsValid()
is like this:
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
}
So just simply call IsValid()
to verify the struct:
req := &api.Request{
//....
}
err := req.IsValid()
if err != nil {
//invalid ....
}
//valid ...
Supported Validators
The verification order is subject to the definition order,‘in’ and ‘not_in’ can be defined multiple times, whichever occurs first.
number
Including i8, i16, i32, i64, double.
- const, must be the specified value.
- lt, le, gt, ge, represents less than, less than or equal to, greater than, greater than or equal to.
- in, not_in, represents values that can be used and values that cannot be used, and can be specified multiple times, one value at a time.
- not_nil, the field cannot be empty (only valid if the field is 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, must be the specified value.
- min_size, max_size.
- pattern, is used for regular matching.
- prefix, suffix, contains, not_contains.
- in, not_in, respectively indicate that the numerical value used and the numerical value cannot be specified at the same time. It can be specified for one use, and one can be specified.
- not_nil, the field cannot be empty (only valid if the field is optional).
struct StringDemo {
1: string Uninitialized (vt.const = "test")
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, must be the specified value.
- not_nil, the field cannot be empty (only valid if the field is optional).
struct BoolDemo {
1: bool AMD (vt.const = "true")
2: optional bool Nvidia (vt.not_nil = "false")
}
enum
- const, must be the specified value.
- defined_only, must be in the value defined in the enum.
- not_nil, the field cannot be empty (only valid if the field is 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, element constraints.
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, means it can’t be nil when value is a pointer.
- 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, means skipping recursive checks for struct/union/exception. (Defaults to false when used as a separate field, true by default when used as an element).
- not_nil, the field cannot be empty (only valid if the field is optional).
struct OuterRequest {
1: SomeStruct Struct (vt.skip = "true")
2: SomeUnion Union (vt.skip = "true")
3: SomeStruct NotNilStruct (vt.not_nil = "true")
}
variable reference
The prefix ‘$’ represents a reference to a variable, which can be used for cross-field verification:
- $x represents a variable named x, the variable name is [a-zA-Z0-9_], and its scope rule is **current structure**.
- $ represents the current field where the validator is located.
struct Example {
1: string A (vt.max_size = "$C")
2: string B (vt.not_in = "$A")
3: i32 C
}
utility function
The prefix ‘@’ indicates the built-in tool function to calculate the check value. Currently supported tool functions:
- sprintf(fmt, $1, $2…), used to output specific characters.
- len($x), output variable size (string length, number of list elements).
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\")")
}