mwitkow/go-proto-validators

GitHub: mwitkow/go-proto-validators

一个 protoc 插件,通过 .proto 文件中的字段注解为 Go 结构体自动生成数据验证函数,避免运行时反射开销。

Stars: 1102 | Forks: 163

# Golang ProtoBuf Validator 编译器 [![Travis 构建](https://travis-ci.org/mwitkow/go-proto-validators.svg)](https://travis-ci.org/mwitkow/go-proto-validators) [![Apache 2.0 许可证](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](LICENSE) 一个 `protoc` 插件,根据 `.proto` 文件中的字段选项,为 Go proto `struct` 生成 `Validate() error` 函数。 验证函数是通过代码生成的,因此不会因为基于标签的反射在深度嵌套的 message 上而影响性能。 ## 环境要求 目前验证使用 Protobuf validator 可在以下版本中正常工作: - Go 1.11, 1.12, 1.13 - [Protobuf](https://github.com/protocolbuffers/protobuf) @ `v3.8.0` - [Go Protobuf](https://github.com/golang/protobuf) @ `v1.3.2` - [Gogo Protobuf](https://github.com/gogo/protobuf) @ `v1.3.0` 在早期 Go 版本的项目中_应该_仍然可以使用它。但是,如果你想为本代码库做贡献,出于对 Go module 的支持,你至少需要 1.11 版本。 ## 给我展示下代码 让我们来看看下面这段 `proto3` 代码片段: ``` syntax = "proto3"; package validator.examples; import "github.com/mwitkow/go-proto-validators/validator.proto"; message InnerMessage { // some_integer can only be in range (0, 100). int32 some_integer = 1 [(validator.field) = {int_gt: 0, int_lt: 100}]; // some_float can only be in range (0;1). double some_float = 2 [(validator.field) = {float_gte: 0, float_lte: 1}]; } message OuterMessage { // important_string must be a lowercase alpha-numeric of 5 to 30 characters (RE2 syntax). string important_string = 1 [(validator.field) = {regex: "^[a-z0-9]{5,30}$"}]; // proto3 doesn't have `required`, the `msg_exist` enforces presence of InnerMessage. InnerMessage inner = 2 [(validator.field) = {msg_exists : true}]; } ``` 首先,对于 `proto3` 来说,**`required` 关键字又回来了**,它伪装成了 `msg_exists`。繁琐的 `if-nil` 检查有救了! 其次,字段的期望值现在成了 `.proto` 契约文件的一部分。再也不用到代码中去寻找判断条件了! 第三,生成的代码易于理解且带有清晰的错误提示信息。请看: ``` func (this *InnerMessage) Validate() error { if !(this.SomeInteger > 0) { return fmt.Errorf("validation error: InnerMessage.SomeInteger must be greater than '0'") } if !(this.SomeInteger < 100) { return fmt.Errorf("validation error: InnerMessage.SomeInteger must be less than '100'") } if !(this.SomeFloat >= 0) { return fmt.Errorf("validation error: InnerMessage.SomeFloat must be greater than or equal to '0'") } if !(this.SomeFloat <= 1) { return fmt.Errorf("validation error: InnerMessage.SomeFloat must be less than or equal to '1'") } return nil } var _regex_OuterMessage_ImportantString = regexp.MustCompile("^[a-z0-9]{5,30}$") func (this *OuterMessage) Validate() error { if !_regex_OuterMessage_ImportantString.MatchString(this.ImportantString) { return fmt.Errorf("validation error: OuterMessage.ImportantString must conform to regex '^[a-z0-9]{5,30}$'") } if nil == this.Inner { return fmt.Errorf("validation error: OuterMessage.Inner message must exist") } if this.Inner != nil { if err := validators.CallValidatorIfExists(this.Inner); err != nil { return err } } return nil } ``` ## 安装与使用 `protoc` 编译器期望在执行的 `$PATH` 中找到名为 `proto-gen-XYZ` 的插件。所以首先是: ``` export PATH=${PATH}:${GOPATH}/bin ``` 然后,进行常规操作 ``` go get github.com/mwitkow/go-proto-validators/protoc-gen-govalidators ``` 你的 `protoc` 构建过程可能看起来非常简单,就像这样: ``` protoc \ --proto_path=. \ --go_out=. \ *.proto ``` 这很好,直到你遇到 `.proto` 的 include。因为 `go-proto-validators` 在 `.proto` 文件内部使用了字段选项,它的 `.proto` 定义(以及 Google 的 `descriptor.proto` 本身)需要位于 `protoc` 的 include 路径中。因此,上面的命令就变成了: ``` protoc \ --proto_path=${GOPATH}/src \ --proto_path=${GOPATH}/src/github.com/google/protobuf/src \ --proto_path=. \ --go_out=. \ --govalidators_out=. \ *.proto ``` 或者使用 gogo protobufs: ``` protoc \ --proto_path=${GOPATH}/src \ --proto_path=${GOPATH}/src/github.com/gogo/protobuf/protobuf \ --proto_path=. \ --gogo_out=. \ --govalidators_out=gogoimport=true:. \ *.proto ``` 基本上,魔法般的咒语(除了 include 之外)就是 `--govalidators_out`。它会触发 `protoc-gen-govalidators` 插件去生成 `mymessage.validator.pb.go`。就是这样 :) ## 许可证 `go-proto-validators` 基于 Apache 2.0 许可证发布。详情请参阅 [LICENSE](LICENSE) 文件。
标签:EVTX分析, 日志审计