protoc-gen-validator
is a protoc plugin that generates validation functions for Go structs, consistent in usage and functionality with the Thrift version of Validator. Developers can reference the annotation file extended by protoc-gen-validator
and use these annotations to declare constraints on protobuf
message
and field
. Finally, protoc-gen-validator
will generate a Validate() error
method for the corresponding message
.
Example:
message Example {
int64 Int64Const = 1 [(api.vt).const="123"]; // The value of 'Int64Const' must be equal to '123'
optional double DoubleLe = 2 [(api.vt).le="123.45"]; // The value of 'DoubleLe' must be less than '123.45'
optional bool BoolConst = 3 [(api.vt).const="true"]; // The value of 'BoolConst' must be 'true'
optional string StringMaxSize = 4 [(api.vt).max_size="12"]; // The maximum length of 'StringMaxSize' is '12'
optional bytes bytesPrefix = 5 [(api.vt).prefix="validator"]; // The prefix of 'bytesPrefix' must be 'validator'
repeated string ListElem = 6 [(api.vt).elem.const="validator"]; // Each element of 'ListElem' must be 'validator'
// All values of 'MapKeyValue' keys must be '123'
// All values of 'MapKeyValue' keys must be greater than '100'
// All values of 'MapKeyValue' values must be 'validator'
// All prefixes of 'MapKeyValue' values must be 'validator'
map<int32, string> MapKeyValue = 7 [(api.vt).key.const="123", (api.vt).key.gt="100", (api.vt).value.const="validator", (api.vt).value.prefix="validator"];
optional int64 Func1 = 8 [(api.vt).gt = "@add($Int64Const, 1000)"]; // Use the built-in function 'add' to get the constraint value
}
Generated method:
func (m *Example) Validate() error {
if m.GetInt64Const() != int64(123) {
return fmt.Errorf("field Int64Const not match const value, current value: %v", m.GetInt64Const())
}
if m.GetDoubleLe() > float64(123.45) {
return fmt.Errorf("field DoubleLe le rule failed, current value: %v", m.GetDoubleLe())
}
if m.GetBoolConst() != true {
return fmt.Errorf("field BoolConst const rule failed, current value: %v", m.GetBoolConst())
}
if len(m.GetStringMaxSize()) > int(12) {
return fmt.Errorf("field StringMaxSize max_len rule failed, current value: %d", len(m.GetStringMaxSize()))
}
_src := []byte("validator")
if !bytes.HasPrefix(m.GetBytesPrefix(), _src) {
return fmt.Errorf("field bytesPrefix prefix rule failed, current value: %v", m.GetBytesPrefix())
}
for i := 0; i < len(m.GetListElem()); i++ {
_elem := m.GetListElem()[i]
_src1 := "validator"
if _elem != _src1 {
return fmt.Errorf("field _elem not match const value, current value: %v", _elem)
}
}
for k := range m.GetMapKeyValue() {
if k != int32(123) {
return fmt.Errorf("field k not match const value, current value: %v", k)
}
if k <= int32(100) {
return fmt.Errorf("field k gt rule failed, current value: %v", k)
}
}
for _, v := range m.GetMapKeyValue() {
_src2 := "validator"
if v != _src2 {
return fmt.Errorf("field v not match const value, current value: %v", v)
}
_src3 := "validator"
if !strings.HasPrefix(v, _src3) {
return fmt.Errorf("field v prefix rule failed, current value: %v", v)
}
}
_src4 := m.GetInt64Const() + int64(1000)
if m.GetFunc1() <= int64(_src4) {
return fmt.Errorf("field Func1 gt rule failed, current value: %v", m.GetFunc1())
}
return nil
}
$PATH
$PATH
/$GOPATH
protoc-gen-validator
should be in $PATH
/$GOPATH
proto2
/proto3
syntax, with a recommendation to use proto3
syntaxExecute the following command:
go install github.com/cloudwego/protoc-gen-validator@latest
version
: Print the version of protoc-gen-validator
recurse
: Recursively generate validation functions for dependent proto filesfunc
: Specify the location of custom validation functionsThe generated validation function (example_validate.pb.go) is in the same location as protoc-gen-go.
cd example
protoc \
-I . \
--go_out=. \
--validator_out=. \
example.proto
By default, the generated path for example_validate.pb.go
is consistent with option go_package
. You can use the paths=source_relative:.
parameter to generate it in the location you want.
cd example
protoc \
-I . \
--go_out=. \
--go_opt=source_relative \
--validator_out=. \
--validator_opt=source_relative \
example.proto
If you want to specify the go module
, it needs to be consistent with the prefix of option go_package
, for example:
// option go_package="example.com/validator/example";
cd example
protoc \
-I . \
--go_out=. \
--go_opt=module=example.com/validator \
--validator_out=. \
--validator_opt=module=example.com/validator \
example.proto
Currently, protoc-gen-validator only supports basic protobuf data types, and some WKTs types, such as Any, Oneofs, etc., will be supported gradually in the future.
The annotation “vt” is an abbreviation for “validate”.
All numeric types (
float
,double
,int32
,int64
,uint32
,uint64
,sint32
,sint64
,fixed32
,fixed64
,sfixed32
,sfixed64
) share the same constraint rules.
int32 Int32Const = 1 [(api.vt).const="123"];
optional double DoubleLe = 2 [(api.vt).le="123.54"];
// Since the 'in' constraint is a list, the syntax here is slightly different
optional fixed32 Fix32In = 3 [(api.vt)={in: ["123","456","789"]}];
optional int64 I64NotNil = 4 [(api.vt).not_nil="true"];
optional bool BoolConst = 1 [(api.vt).const="true"];
optional bool BoolNotNil = 2 [(api.vt).not_nil="true"];
optional string StringConst = 1 [(api.vt).const="validator"];
optional bytes bytesConst = 2 [(api.vt).const="validator"];
optional string StringPattern = 3 [(api.vt).pattern="[0-9A-Za-z]+"];
optional bytes bytesPattern = 4 [(api.vt).pattern="[0-9A-Za-z]+"];
optional string StringMinSize = 5 [(api.vt).min_size="12"];
optional bytes bytesMaxSize = 6 [(api.vt).max_size="12"];
optional string StringPrefix = 7 [(api.vt).prefix="validator"];
optional string StringSuffix = 8 [(api.vt).suffix="validator"];
optional bytes bytesContain = 9 [(api.vt).contains="validator"];
optional bytes bytesNotContain = 10 [(api.vt).not_contains="validator"];
// Since the 'in' constraint is a list, the syntax here is slightly different
optional string StringIn = 11 [(api.vt)={in:["123","456","789"]}];
optional bytes bytesNotIn = 12 [(api.vt)={not_in:["123","456","789"]}];
optional string StringNotNil = 13 [(api.vt).not_nil="true"];
enum EnumType {
TWEET = 0;
RETWEET = 1;
}
optional EnumType Enum1 = 1 [(api.vt).const="EnumType.TWEET"];
optional EnumType Enum2 = 2 [(api.vt).defined_only="true"];
optional EnumType Enum3 = 3 [(api.vt).not_nil="true"];
repeated string ListMinSize = 1 [(api.vt).min_size="12"];
repeated string ListBaseElem = 2 [(api.vt).elem.const="validator"];
map<int32, string> MapISMinSize = 1 [(api.vt).min_size="10", (api.vt).max_size="30"];
map<int32, string> MapKey = 2 [(api.vt).key.const="123", (api.vt).key.gt="12"];
map<int32, string> MapValue = 3 [(api.vt).value.const="validator", (api.vt).value.prefix="validator"]
map<int32, MsgType> MapNoSparse = 4 [(api.vt).no_sparse="true"];
optional MapValidate MsgField = 1 [(api.vt).skip="true"];
message StructValidate {
option (api.msg_vt).assert = "@equal($MsgValidate,1)";
optional int64 MsgValidate = 1;
}
optional double DoubleLe = 1;
optional double Reference = 2 [(api.vt).le="$DoubleLe"];
protoc-gen-validator
provides a set of built-in functions for writing validation rules.
function name | arguments | results | remarks |
---|---|---|---|
len | 1: container filed | 1: length of container (integer) | just like len of go |
sprintf | 1: format string 2+: arguments matching format | 1: formatted string (string) | just like fmt.Sprintf of go |
now_unix_nano | none | 1: nano seconds (int64) | just like time.Now().UnixNano() of go |
equal | 1, 2: comparable values | 1: whether two arguments is equal (bool) | just like == of go |
mod | 1, 2: integer | 1: remainder of 1/1/2 (integer) | just like % of go |
add | 1, 2: both are numeric or string | 1: sum of two arguments (integer or float64 or string) | just like + of go |
protoc-gen-validator
provides a method to extend validation functions. Now you can use the func
parameter to customize your validation functions. For example:
cd example
protoc \
-I . \
--go_out=. \
--validator_out=. \
--validator_opt=func=my_func=path_to_template.txt \
example.proto
my_func
is the name of the function, and path_to_template.txt
is the template file for the function, which should be a standard Go template. Available template variables are as follows:
Variable Name | Meaning | Type |
---|---|---|
Source | variable name that rule will refer to | string |
Function | data of current function | *“github.com/cloudwego/protoc-gen-validator/parser”.ToolFunction |