mwitkow/go-proto-validators
GitHub: mwitkow/go-proto-validators
一个 protoc 插件,通过 .proto 文件中的字段注解为 Go 结构体自动生成数据验证函数,避免运行时反射开销。
Stars: 1102 | Forks: 163
# Golang ProtoBuf Validator 编译器
[](https://travis-ci.org/mwitkow/go-proto-validators)
[](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分析, 日志审计