Go gRPC進階-proto資料驗證(九)
前言
上篇介紹了go-grpc-middleware的grpc_zap
、grpc_auth
和grpc_recovery
使用,本篇將介紹grpc_validator
,它可以對gRPC資料的輸入和輸出進行驗證。
建立proto檔案,新增驗證規則
這裡使用第三方外掛go-proto-validators自動生成驗證規則。
go get github.com/mwitkow/go-proto-validators
1.新建simple.proto檔案
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
外掛,第一篇有介紹。只需要新增"--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
檔案。
// 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
驗證攔截器新增到服務端
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()),
)),
)
執行後,當輸入資料驗證失敗後,會有以下錯誤返回
Call Route err: rpc error: code = InvalidArgument desc = invalid field SomeInteger: value '101' must be less than '100'
其他型別驗證規則設定
enum
驗證
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
驗證
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-exam