https://github.com/envoyproxy/protoc-gen-validate提供了字段检验的功能
还有其它方案:https://www.yuque.com/jw-go/mqm3eb/gy8npv
安装

  • 下载该项目下的validate/validate.proto,与常用的proto文件放一起
  • go get github.com/envoyproxy/protoc-gen-validate

使用

  1. syntax = "proto3";
  2. package examplepb;
  3. import "validate/validate.proto";
  4. message Person {
  5. uint64 id = 1 [(validate.rules).uint64.gt = 999];
  6. string email = 2 [(validate.rules).string.email = true];
  7. string name = 3 [(validate.rules).string = {
  8. pattern: "^[^[0-9]A-Za-z]+( [^[0-9]A-Za-z]+)*$",
  9. max_bytes: 256,
  10. }];
  11. Location home = 4 [(validate.rules).message.required = true];
  12. message Location {
  13. double lat = 1 [(validate.rules).double = { gte: -90, lte: 90 }];
  14. double lng = 2 [(validate.rules).double = { gte: -180, lte: 180 }];
  15. }
  16. }

生成pb.go 与validate.go

  • protoc —proto_path=/Users/lx/gopath/src —proto_path=. \

—go_out=plugins=grpc,paths=source_relative:. \
—validate_out=”lang=go,paths=source_relative:.” \
test.proto

  1. p := new(Person)
  2. err := p.Validate() // err: Id must be greater than 999
  3. p.Id = 1000
  4. err = p.Validate() // err: Email must be a valid email address
  5. p.Email = "example@lyft.com"
  6. err = p.Validate() // err: Name must match pattern '^[^\d\s]+( [^\d\s]+)*$'
  7. p.Name = "Protocol Buffer"
  8. err = p.Validate() // err: Home is required
  9. p.Home = &Location{37.7, 999}
  10. err = p.Validate() // err: Home.Lng must be within [-180, 180]
  11. p.Home.Lng = -122.4
  12. err = p.Validate() // err: nil

Numerics

All numeric types (float, double, int32, int64, uint32, uint64, sint32, sint64, fixed32, fixed64, sfixed32, sfixed64) share the same rules

  1. const =
  2. lt <
  3. lte <=
  4. gt >
  5. gte >=
  6. // x must equal 1.23 exactly
  7. float x = 1 [(validate.rules).float.const = 1.23];
  8. // x must be less than 10
  9. int32 x = 1 [(validate.rules).int32.lt = 10];
  10. // x must be greater than or equal to 20
  11. uint64 x = 1 [(validate.rules).uint64.gte = 20];
  12. // x must be in the range [30, 40)
  13. fixed32 x = 1 [(validate.rules).fixed32 = {gte:30, lt: 40}];
  14. // in/not_in
  15. // x must be either 1, 2, or 3
  16. uint32 x = 1 [(validate.rules).uint32 = {in: [1,2,3]}];
  17. // x cannot be 0 nor 0.99
  18. float x = 1 [(validate.rules).float = {not_in: [0, 0.99]}];

Bools

  1. // x must be set to true
  2. bool x = 1 [(validate.rules).bool.const = true];
  3. // x cannot be set to true
  4. bool x = 1 [(validate.rules).bool.const = false];

Strings

  1. // x must be set to "foo"
  2. string x = 1 [(validate.rules).string.const = "foo"];
  3. // x must be exactly 5 characters long
  4. string x = 1 [(validate.rules).string.len = 5];
  5. // x must be at least 3 characters long
  6. string x = 1 [(validate.rules).string.min_len = 3];
  7. // x must be between 5 and 10 characters, inclusive
  8. string x = 1 [(validate.rules).string = {min_len: 5, max_len: 10}];
  9. // x must be at most 15 bytes long
  10. string x = 1 [(validate.rules).string.max_bytes = 15];
  11. // x must be between 128 and 1024 bytes long
  12. string x = 1 [(validate.rules).string = {min_bytes: 128, max_bytes: 1024}];
  13. // x must be a non-empty, case-insensitive hexadecimal string
  14. string x = 1 [(validate.rules).string.pattern = "(?i)^[0-9a-f]+$"];
  15. // x must begin with "foo"
  16. string x = 1 [(validate.rules).string.prefix = "foo"];
  17. // x must end with "bar"
  18. string x = 1 [(validate.rules).string.suffix = "bar"];
  19. // x must contain "baz" anywhere inside it
  20. string x = 1 [(validate.rules).string.contains = "baz"];
  21. // x cannot contain "baz" anywhere inside it
  22. string x = 1 [(validate.rules).string.not_contains = "baz"];
  23. // x must begin with "fizz" and end with "buzz"
  24. string x = 1 [(validate.rules).string = {prefix: "fizz", suffix: "buzz"}];
  25. // x must end with ".proto" and be less than 64 characters
  26. string x = 1 [(validate.rules).string = {suffix: ".proto", max_len:64}];
  27. // x must be either "foo", "bar", or "baz"
  28. string x = 1 [(validate.rules).string = {in: ["foo", "bar", "baz"]}];
  29. // x cannot be "fizz" nor "buzz"
  30. string x = 1 [(validate.rules).string = {not_in: ["fizz", "buzz"]}];
  1. // x must be a valid email address (via RFC 1034)
  2. string x = 1 [(validate.rules).string.email = true];
  3. // x must be a valid address (IP or Hostname).
  4. string x = 1 [(validate.rules).string.address = true];
  5. // x must be a valid hostname (via RFC 1034)
  6. string x = 1 [(validate.rules).string.hostname = true];
  7. // x must be a valid IP address (either v4 or v6)
  8. string x = 1 [(validate.rules).string.ip = true];
  9. // x must be a valid IPv4 address
  10. // eg: "192.168.0.1"
  11. string x = 1 [(validate.rules).string.ipv4 = true];
  12. // x must be a valid IPv6 address
  13. // eg: "fe80::3"
  14. string x = 1 [(validate.rules).string.ipv6 = true];
  15. // x must be a valid absolute URI (via RFC 3986)
  16. string x = 1 [(validate.rules).string.uri = true];
  17. // x must be a valid URI reference (either absolute or relative)
  18. string x = 1 [(validate.rules).string.uri_ref = true];
  19. // x must be a valid UUID (via RFC 4122)
  20. string x = 1 [(validate.rules).string.uuid = true];
  21. // x must conform to a well known regex for HTTP header names (via RFC 7230)
  22. string x = 1 [(validate.rules).string.well_known_regex = HTTP_HEADER_NAME]
  23. // x must conform to a well known regex for HTTP header values (via RFC 7230)
  24. string x = 1 [(validate.rules).string.well_known_regex = HTTP_HEADER_VALUE];
  25. // x must conform to a well known regex for headers, disallowing \r\n\0 characters.
  26. string x = 1 [(validate.rules).string {well_known_regex: HTTP_HEADER_VALUE, strict: false}];

Bytes

  1. // x must be set to "foo" ("\x66\x6f\x6f")
  2. bytes x = 1 [(validate.rules).bytes.const = "foo"];
  3. // x must be set to "\xf0\x90\x28\xbc"
  4. bytes x = 1 [(validate.rules).bytes.const = "\xf0\x90\x28\xbc"];
  5. // x must be exactly 3 bytes
  6. bytes x = 1 [(validate.rules).bytes.len = 3];
  7. // x must be at least 3 bytes long
  8. bytes x = 1 [(validate.rules).bytes.min_len = 3];
  9. // x must be between 5 and 10 bytes, inclusive
  10. bytes x = 1 [(validate.rules).bytes = {min_len: 5, max_len: 10}];
  11. // x must be a non-empty, ASCII byte sequence
  12. bytes x = 1 [(validate.rules).bytes.pattern = "^[\x00-\x7F]+$"];
  13. // x must begin with "\x99"
  14. bytes x = 1 [(validate.rules).bytes.prefix = "\x99"];
  15. // x must end with "buz\x7a"
  16. bytes x = 1 [(validate.rules).bytes.suffix = "buz\x7a"];
  17. // x must contain "baz" anywhere inside it
  18. bytes x = 1 [(validate.rules).bytes.contains = "baz"];
  19. // x must be either "foo", "bar", or "baz"
  20. bytes x = 1 [(validate.rules).bytes = {in: ["foo", "bar", "baz"]}];
  21. // x cannot be "fizz" nor "buzz"
  22. bytes x = 1 [(validate.rules).bytes = {not_in: ["fizz", "buzz"]}];
  23. // x must be a valid IP address (either v4 or v6) in byte format
  24. bytes x = 1 [(validate.rules).bytes.ip = true];
  25. // x must be a valid IPv4 address in byte format
  26. // eg: "\xC0\xA8\x00\x01"
  27. bytes x = 1 [(validate.rules).bytes.ipv4 = true];
  28. // x must be a valid IPv6 address in byte format
  29. // eg: "\x20\x01\x0D\xB8\x85\xA3\x00\x00\x00\x00\x8A\x2E\x03\x70\x73\x34"
  30. bytes x = 1 [(validate.rules).bytes.ipv6 = true];

Enums

All literal values should use the numeric (int32) value as defined in the enum descriptor.

  1. enum State {
  2. INACTIVE = 0;
  3. PENDING = 1;
  4. ACTIVE = 2;
  5. }
  6. // x must be set to ACTIVE (2)
  7. State x = 1 [(validate.rules).enum.const = 2];
  8. // x can only be INACTIVE, PENDING, or ACTIVE
  9. State x = 1 [(validate.rules).enum.defined_only = true];
  10. // x must be either INACTIVE (0) or ACTIVE (2)
  11. State x = 1 [(validate.rules).enum = {in: [0,2]}];
  12. // x cannot be PENDING (1)
  13. State x = 1 [(validate.rules).enum = {not_in: [1]}];

Messages

If a field contains a message and the message has been generated with PGV, validation will be performed recursively. Message’s not generated with PGV are skipped
一般来讲,pb.go这个存根文件不会由PGV生成

  1. // if Person was generated with PGV and x is set,
  2. // x's fields will be validated.
  3. Person x = 1;
  1. // The fields on Person x will not be validated.
  2. Person x = 1 [(validate.rules).message.skip = true];
  3. // x cannot be unset
  4. Person x = 1 [(validate.rules).message.required = true];
  5. // x cannot be unset, but the validations on x will not be performed
  6. Person x = 1 [(validate.rules).message = {required: true, skip: true}];

Repeated

  1. // x must contain at least 3 elements
  2. repeated int32 x = 1 [(validate.rules).repeated.min_items = 3];
  3. // x must contain between 5 and 10 Persons, inclusive
  4. repeated Person x = 1 [(validate.rules).repeated = {min_items: 5, max_items: 10}];
  5. // x must contain exactly 7 elements
  6. repeated double x = 1 [(validate.rules).repeated = {min_items: 7, max_items: 7}];
  7. // x must contain unique int64 values
  8. repeated int64 x = 1 [(validate.rules).repeated.unique = true];
  9. items: 该规则指定了应该应用于字段中的每个元素的约束。除非在此约束上指定了skip,否则重复的消息字段也会
  10. 应用其验证规则
  11. // x must contain positive float values
  12. repeated float x = 1 [(validate.rules).repeated.items.float.gt = 0];
  13. // x must contain Persons but don't validate them
  14. repeated Person x = 1 [(validate.rules).repeated.items.message.skip = true];

Maps

  1. // x must contain at most 3 KV pairs
  2. map<string, uint64> x = 1 [(validate.rules).map.min_pairs = 3];
  3. // x must contain between 5 and 10 KV pairs
  4. map<string, string> x = 1 [(validate.rules)].map = {min_pairs: 5, max_pairs: 10}];
  5. // x must contain exactly 7 KV pairs
  6. map<string, Person> x = 1 [(validate.rules)].map = {min_pairs: 7, max_pairs: 7}];
  7. // all values in x must be set
  8. map<uint64, Person> x = 1 [(validate.rules).map.no_sparse = true];
  9. // keys : 该规则指定了应用于字段中的键的约束
  10. <sint32, string> x = [(validate.rules).map.keys.sint32.lt = 0];
  11. // x must contain strings of at least 3 characters
  12. map<string, string> x = 1 [(validate.rules).map.values.string.min_len = 3];
  13. // x must contain Persons but doesn't validate them
  14. map<string, Person> x = 1 [(validate.rules).map.values.message.skip = true];

Message-Global

  1. disabled:消息上字段的所有验证规则都可以作废,包括支持验证本身的任何消息字段
  2. message Person {
  3. option (validate.disabled) = true;
  4. // x will not be required to be greater than 123
  5. uint64 x = 1 [(validate.rules).uint64.gt = 123];
  6. // y's fields will not be validated
  7. Person y = 2;
  8. }
  9. ignored:不要为此消息生成验证方法或任何相关的验证代码
  10. message Person {
  11. option (validate.ignored) = true;
  12. // x will not be required to be greater than 123
  13. uint64 x = 1 [(validate.rules).uint64.gt = 123];
  14. // y's fields will not be validated
  15. Person y = 2;
  16. }

OneOfs

  1. oneof id {
  2. // either x, y, or z must be set.
  3. option (validate.required) = true;
  4. string x = 1;
  5. int32 y = 2;
  6. Person z = 3;
  7. }

第三方类型

Anys

  1. // x cannot be unset
  2. google.protobuf.Any x = 1 [(validate.rules).any.required = true];
  3. // x must not be the Duration or Timestamp WKT
  4. google.protobuf.Any x = 1 [(validate.rules).any = {not_in: [
  5. "type.googleapis.com/google.protobuf.Duration",
  6. "type.googleapis.com/google.protobuf.Timestamp"
  7. ]}];

Durations

  1. // x cannot be unset
  2. google.protobuf.Duration x = 1 [(validate.rules).duration.required = true];
  3. // x must equal 1.5s exactly
  4. google.protobuf.Duration x = 1 [(validate.rules).duration.const = {
  5. seconds: 1,
  6. nanos: 500000000
  7. }];
  8. // x must be less than 10s
  9. google.protobuf.Duration x = 1 [(validate.rules).duration.lt.seconds = 10];
  10. // x must be greater than or equal to 20ns
  11. google.protobuf.Duration x = 1 [(validate.rules).duration.gte.nanos = 20];
  12. // x must be in the range [0s, 1s)
  13. google.protobuf.Duration x = 1 [(validate.rules).duration = {
  14. gte: {},
  15. lt: {seconds: 1}
  16. }];
  17. // 将lt(e)和gt(e)的值反向是有效的,并创建一个排他范围
  18. // x must be outside the range [0s, 1s)
  19. google.protobuf.Duration x = 1 [(validate.rules).duration = {
  20. lt: {},
  21. gte: {seconds: 1}
  22. }];
  23. // x must be either 0s or 1s
  24. google.protobuf.Duration x = 1 [(validate.rules).duration = {in: [
  25. {},
  26. {seconds: 1}
  27. ]}];
  28. // x cannot be 20s nor 500ns
  29. google.protobuf.Duration x = 1 [(validate.rules).duration = {not_in: [
  30. {seconds: 20},
  31. {nanos: 500}
  32. ]}];

Timestamps

  1. // x cannot be unset
  2. google.protobuf.Timestamp x = 1 [(validate.rules).timestamp.required = true];
  3. // x must equal 2009/11/10T23:00:00.500Z exactly
  4. google.protobuf.Timestamp x = 1 [(validate.rules).timestamp.const = {
  5. seconds: 63393490800,
  6. nanos: 500000000
  7. }];
  8. // x must be less than the Unix Epoch
  9. google.protobuf.Timestamp x = 1 [(validate.rules).timestamp.lt.seconds = 0];
  10. // x must be greater than or equal to 2009/11/10T23:00:00Z
  11. google.protobuf.Timestamp x = 1 [(validate.rules).timestamp.gte.seconds = 63393490800];
  12. // x must be in the range [epoch, 2009/11/10T23:00:00Z)
  13. google.protobuf.Timestamp x = 1 [(validate.rules).timestamp = {
  14. gte: {},
  15. lt: {seconds: 63393490800}
  16. }];
  17. //将lt(e)和gt(e)的值反向是有效的,并创建一个排他范围
  18. // x must be outside the range [epoch, 2009/11/10T23:00:00Z)
  19. google.protobuf.Timestamp x = 1 [(validate.rules).timestamp = {
  20. lt: {},
  21. gte: {seconds: 63393490800}
  22. }];
  23. //lt_now/gt_now: these inequalities allow for ranges relative to the current time
  24. // x must be less than the current timestamp
  25. google.protobuf.Timestamp x = 1 [(validate.rules).timestamp.lt_now = true];
  26. // x must be less than the current timestamp
  27. google.protobuf.Timestamp x = 1 [(validate.rules).timestamp.gt_now = true];
  28. // within:指定该字段的值应该在当前时间的持续时间内。该规则可以与lt_now和gt_now一起使用来控制这些范围
  29. // x must be within ±1s of the current time
  30. google.protobuf.Timestamp x = 1 [(validate.rules).timestamp.within.seconds = 1];
  31. // x must be within the range (now, now+1h)
  32. google.protobuf.Timestamp x = 1 [(validate.rules).timestamp = {
  33. gt_now: true,
  34. within: {seconds: 3600}
  35. }];