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


    前言

    上篇介绍了go-grpc-middlewaregrpc_zapgrpc_authgrpc_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/tree/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.gosimple.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-middlewaregrpc_validator集成go-proto-validators,我们只需要在编写proto时设好验证规则,并把grpc_validator添加到gRPC服务端,就能完成gRPC的数据验证,很简单也很方便。

    教程源码地址:https://github.com/Bingjian-Zhu/go-grpc-example

  • 相关阅读:
    句柄
    类,方法,抽象方法,接口
    Enum类型
    String 为什么是不可变的
    大数据怎么排序
    oracle创建表空间 导入数据库
    eclipse常见小问题
    eclipse创建项目
    存储过程
    在VMware通过挂载系统光盘搭建本地yum仓库
  • 原文地址:https://www.cnblogs.com/FireworksEasyCool/p/12761033.html
Copyright © 2020-2023  润新知