Go gRPC进阶-proto数据验证(九)

939496716 · · 1062 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

[原文地址](https://bingjian-zhu.github.io/2020/04/23/Go-gRPC%E8%BF%9B%E9%98%B6-proto%E6%95%B0%E6%8D%AE%E9%AA%8C%E8%AF%81%EF%BC%88%E4%B9%9D%EF%BC%89/) ### 前言 上篇介绍了[go-grpc-middleware](https://github.com/grpc-ecosystem/go-grpc-middleware)的`grpc_zap`、`grpc_auth`和`grpc_recovery`使用,本篇将介绍`grpc_validator`,它可以对gRPC数据的输入和输出进行验证。 ### 创建proto文件,添加验证规则 这里使用第三方插件[go-proto-validators](https://github.com/mwitkow/go-proto-validators)自动生成验证规则。 `go get github.com/mwitkow/go-proto-validators` 1.新建simple.proto文件 ```protobuf syntax = "proto3"; package proto; import "github.com/mwitkow/go-proto-validators/validator.proto"; message InnerMessage { // some_integer can only be in range (1, 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-z]{2,5}$"}]; // proto3 doesn't have `required`, the `msg_exist` enforces presence of InnerMessage. InnerMessage inner = 2 [(validator.field) = {msg_exists : true}]; } service Simple{ rpc Route (InnerMessage) returns (OuterMessage){}; } ``` 代码`import "github.com/mwitkow/go-proto-validators/validator.proto"`,文件`validator.proto`需要`import "google/protobuf/descriptor.proto";`包,不然会报错。 `google/protobuf`地址:https://github.com/protocolbuffers/protobuf/blob/master/src/google/protobuf/descriptor.proto。 把`src`文件夹中的`protobuf`目录下载到GOPATH目录下。 2.编译simple.proto文件 `go get github.com/mwitkow/go-proto-validators/protoc-gen-govalidators` 指令编译:`protoc --govalidators_out=. --go_out=plugins=grpc:./ ./simple.proto` > 或者使用`VSCode-proto3`插件,[第一篇](https://www.cnblogs.com/FireworksEasyCool/p/12669371.html)有介绍。只需要添加`"--govalidators_out=."`即可。 ``` // vscode-proto3插件配置 "protoc": { // protoc.exe所在目录 "path": "C:\\Go\\bin\\protoc.exe", // 保存时自动编译 "compile_on_save": true, "options": [ // go编译输出指令 "--go_out=plugins=grpc:.", "--govalidators_out=." ] }, ``` 编译完成后,自动生成`simple.pb.go`和`simple.validator.pb.go`文件,`simple.pb.go`文件不再介绍,我们看下`simple.validator.pb.go`文件。 ```go // Code generated by protoc-gen-gogo. DO NOT EDIT. // source: go-grpc-example/9-grpc_proto_validators/proto/simple.proto package proto import ( fmt "fmt" math "math" proto "github.com/golang/protobuf/proto" _ "github.com/mwitkow/go-proto-validators" regexp "regexp" github_com_mwitkow_go_proto_validators "github.com/mwitkow/go-proto-validators" ) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal var _ = fmt.Errorf var _ = math.Inf func (this *InnerMessage) Validate() error { if !(this.SomeInteger > 0) { return github_com_mwitkow_go_proto_validators.FieldError("SomeInteger", fmt.Errorf(`value '%v' must be greater than '0'`, this.SomeInteger)) } if !(this.SomeInteger < 100) { return github_com_mwitkow_go_proto_validators.FieldError("SomeInteger", fmt.Errorf(`value '%v' must be less than '100'`, this.SomeInteger)) } if !(this.SomeFloat >= 0) { return github_com_mwitkow_go_proto_validators.FieldError("SomeFloat", fmt.Errorf(`value '%v' must be greater than or equal to '0'`, this.SomeFloat)) } if !(this.SomeFloat <= 1) { return github_com_mwitkow_go_proto_validators.FieldError("SomeFloat", fmt.Errorf(`value '%v' must be lower than or equal to '1'`, this.SomeFloat)) } return nil } var _regex_OuterMessage_ImportantString = regexp.MustCompile(`^[a-z]{2,5}$`) func (this *OuterMessage) Validate() error { if !_regex_OuterMessage_ImportantString.MatchString(this.ImportantString) { return github_com_mwitkow_go_proto_validators.FieldError("ImportantString", fmt.Errorf(`value '%v' must be a string conforming to regex "^[a-z]{2,5}$"`, this.ImportantString)) } if nil == this.Inner { return github_com_mwitkow_go_proto_validators.FieldError("Inner", fmt.Errorf("message must exist")) } if this.Inner != nil { if err := github_com_mwitkow_go_proto_validators.CallValidatorIfExists(this.Inner); err != nil { return github_com_mwitkow_go_proto_validators.FieldError("Inner", err) } } return nil } ``` 里面自动生成了`message`中属性的验证规则。 ### 把`grpc_validator`验证拦截器添加到服务端 ```go grpcServer := grpc.NewServer(cred.TLSInterceptor(), grpc.StreamInterceptor(grpc_middleware.ChainStreamServer( grpc_validator.StreamServerInterceptor(), grpc_auth.StreamServerInterceptor(auth.AuthInterceptor), grpc_zap.StreamServerInterceptor(zap.ZapInterceptor()), grpc_recovery.StreamServerInterceptor(recovery.RecoveryInterceptor()), )), grpc.UnaryInterceptor(grpc_middleware.ChainUnaryServer( grpc_validator.UnaryServerInterceptor(), grpc_auth.UnaryServerInterceptor(auth.AuthInterceptor), grpc_zap.UnaryServerInterceptor(zap.ZapInterceptor()), grpc_recovery.UnaryServerInterceptor(recovery.RecoveryInterceptor()), )), ) ``` 运行后,当输入数据验证失败后,会有以下错误返回 ```powershell Call Route err: rpc error: code = InvalidArgument desc = invalid field SomeInteger: value '101' must be less than '100' ``` ### 其他类型验证规则设置 `enum`验证 ```protobuf syntax = "proto3"; package proto; import "github.com/mwitkow/go-proto-validators/validator.proto"; message SomeMsg { Action do = 1 [(validator.field) = {is_in_enum : true}]; } enum Action { ALLOW = 0; DENY = 1; CHILL = 2; } ``` `UUID`验证 ```protobuf syntax = "proto3"; package proto; import "github.com/mwitkow/go-proto-validators/validator.proto"; message UUIDMsg { // user_id must be a valid version 4 UUID. string user_id = 1 [(validator.field) = {uuid_ver: 4, string_not_empty: true}]; } ``` ### 总结 `go-grpc-middleware`中`grpc_validator`集成`go-proto-validators`,我们只需要在编写proto时设好验证规则,并把`grpc_validator`添加到gRPC服务端,就能完成gRPC的数据验证,很简单也很方便。 教程源码地址:https://github.com/Bingjian-Zhu/go-grpc-example

有疑问加站长微信联系(非本文作者))

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

1062 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传