• proto数据验证


    proto数据验证

    一、前言

    上篇介绍了go-grpc-middlewaregrpc_zapgrpc_authgrpc_recovery使用,本篇将介绍grpc_validator,它可以对gRPC数据的输入和输出进行验证。https://github.com/envoyproxy/protoc-gen-validate 待验证

    源码

    二、创建proto文件,添加验证规则

    这里使用第三方插件go-proto-validators自动生成验证规则。

    安装:

    go get github.com/mwitkow/go-proto-validators
    

    1.新建simple.proto文件

    syntax = "proto3";// 协议为proto3
    
    //option go_package = "path;name";
    //path 表示生成的go文件的存放地址,会自动生成目录的。
    //name 表示生成的go文件所属的包名
    
    
    //    protoc -I G:\goproject\src -I ./ --go_out=plugins=grpc:. ./test.proto https://www.cnblogs.com/yisany/p/14875488.html//
    //    protoc -I G:\goproject\src   -I ./ --grpc-gateway_out=logtostderr=true:. .\proto\test.proto
    
    
    //    protoc --govalidators_out=. --go_out=plugins=grpc:./ ./simple.proto
    //    protoc -I ./  --govalidators_out=.\10protoValidators\proto\ --go_out=plugins=grpc:.\10protoValidators\proto\  .\10protoValidators\proto\simple.proto
    
    option go_package = "./;proto";
    package proto;
    
    // import "github.com/mwitkow/go-proto-validators/validator.proto";
    
    import "10protoValidators/proto/google/validator/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){};
    }
    

    执行编译命令:

    protoc -I ./  --govalidators_out=.\10protoValidators\proto\ --go_out=plugins=grpc:.\10protoValidators\proto\  .\10protoValidators\proto\simple.proto
    

    执行编译命令报错遇到一下问题解决方案

    image-20220428205017130

    image-20220508231900689

    三、import google/protobuf/descriptor.proto was not found or had errors. 解决方案

    执行编译报错如下:

    问题1: Import "google/protobuf/descriptor.proto" was not found or had errors.

    image-20220428210438914

    image-20220428210341314

    解决方案:

    1. 到官方下载descriptor.proto文件,保存到本地,下载google/protobuf地址;

    image-20220428210933076

    1. 在创建proto目录下面创建google/protobuf目录并将将下载的descriptor.proto文件移动到该目录下面;

    image-20220428211051521

    问题2: github.com/mwitkow/go-proto-validators/validator.proto: File not found.

    github.com/mwitkow/go-proto-validators/validator.proto: File not found.
    09protoValidators/proto/simple.proto:15:1: Import "github.com/mwitkow/go-proto-validators/validator.proto" was not found or had errors
    

    image-20220428211525161

    1. 到官方下载validator.proto文件,保存到本地,下载validator.proto地址;

      image-20220428211808328

    2. 在创建proto目录下面创建google/validator目录并将将下载的validator.proto文件移动到该目录下面;

    image-20220428211951541

    1. 修改google/protobuf/descriptor.proto路径改为问题1下载的descriptor.proto路径

      // 修改前
      import "google/protobuf/descriptor.proto"
      //  修改后
      import "10protoValidators/proto/google/protobuf/descriptor.proto";
      

    image-20220428212906280

    最后最后修改simple.proto文件validator.proto导入

    image-20220428212410258

    再次执行下面命令

     # 在项目目录下一层级执行 cd grpc
     protoc -I ./  --govalidators_out=.\10protoValidators\proto\ --go_out=plugins=grpc:.\10protoValidators\proto\  .\10protoValidators\proto\simple.proto
    

    编译完成后,自动生成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 !(https://img2022.cnblogs.com/blog/1739642/202205/1739642-20220515174944366-470187240.png 'this.SomeInteger > 0') {
    		return github_com_mwitkow_go_proto_validators.FieldError("SomeInteger", fmt.Errorf(`value '%v' must be greater than '0'`, this.SomeInteger))
    	}
    	if !(https://img2022.cnblogs.com/blog/1739642/202205/1739642-20220515174944774-868825746.png 'this.SomeInteger < 100') {
    		return github_com_mwitkow_go_proto_validators.FieldError("SomeInteger", fmt.Errorf(`value '%v' must be less than '100'`, this.SomeInteger))
    	}
    	if !(https://img2022.cnblogs.com/blog/1739642/202205/1739642-20220515174945183-1110311093.png '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 !(https://img2022.cnblogs.com/blog/1739642/202205/1739642-20220515174945606-134640571.png '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(https://img2022.cnblogs.com/blog/1739642/202205/1739642-20220515174945967-1159649638.png '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}$"`, https://img2022.cnblogs.com/blog/1739642/202205/1739642-20220515174945967-1159649638.png '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验证拦截器添加到服务端

    客户端

    package main
    
    import (
    	"context"
    	pb "go-grpc-example/10protoValidators/proto"
    	"log"
    
    	"google.golang.org/grpc/credentials/insecure"
    
    	"google.golang.org/grpc"
    )
    
    /*
    @author RandySun
    @create 2022-05-08-23:20
    */
    
    // Address 连接地址
    const Address string = ":8000"
    
    var grpcClient pb.SimpleClient
    
    func main() {
    	//从输入的证书文件中为客户端构造TLS凭证
    
    	// 连接服务器 grpc.WithTransportCredentials(insecure.NewCredentials())
    	conn, err := grpc.Dial(Address, grpc.WithTransportCredentials(insecure.NewCredentials()))
    	if err != nil {
    		log.Fatalf("net.Connect err: %v", err)
    	}
    	defer conn.Close()
    
    	// 建立gRPC连接
    	grpcClient = pb.NewSimpleClient(conn)
    	route()
    }
    
    // route 调用服务端Route方法
    func route() {
    	// 创建发送结构体
    	req := pb.InnerMessage{
    		SomeInteger: 200,
    		SomeFloat:   1,
    	}
    	// 调用我们的服务(Route方法)
    	// 同时传入了一个 context.Context ,在有需要时可以让我们改变RPC的行为,比如超时/取消一个正在运行的RPC
    	res, err := grpcClient.Route(context.Background(), &req)
    	if err != nil {
    		log.Fatalf("Call Route err: %v", err)
    	}
    	// 打印返回值
    	log.Println(res)
    }
    
    

    服务端:

    package main
    
    import (
    	"context"
    	pb "go-grpc-example/10protoValidators/proto"
    	"log"
    	"net"
    
    	grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware"
    	grpc_validator "github.com/grpc-ecosystem/go-grpc-middleware/validator"
    	"google.golang.org/grpc"
    )
    
    /*
    @author RandySun
    @create 2022-05-08-23:20
    */
    
    // SimpleService 定义我们的服务
    type SimpleService struct{}
    
    // Route 实现Route方法
    func (s *SimpleService) Route(ctx context.Context, req *pb.InnerMessage) (*pb.OuterMessage, error) {
    	// 添加拦截器后,方法里省略Token认证
    	// //检测Token是否有效
    	// if err := Check(ctx); err != nil {
    	// 	return nil, err
    	// }
    	res := pb.OuterMessage{
    		ImportantString: "hello grpc validator",
    		Inner:           req,
    	}
    	return &res, nil
    }
    
    const (
    	// Address 监听地址
    	Address string = ":8000"
    	// Network 网络通信协议
    	Network string = "tcp"
    )
    
    func main() {
    	// 监听本地端口
    	listener, err := net.Listen(Network, Address)
    	if err != nil {
    		log.Fatalf("net.Listen err: %v", err)
    	}
    
    	// 新建gRPC服务器实例
    	grpcServer := grpc.NewServer(
    		grpc.StreamInterceptor(grpc_middleware.ChainStreamServer(
    			grpc_validator.StreamServerInterceptor(), // 校验器
    
    		)),
    		grpc.UnaryInterceptor(grpc_middleware.ChainUnaryServer(
    			grpc_validator.UnaryServerInterceptor(), // 校验器
    
    		)),
    	)
    	// 在gRPC服务器注册我们的服务
    	pb.RegisterSimpleServer(grpcServer, &SimpleService{})
    	log.Println(Address + " net.Listing whth TLS and token...")
    	//用服务器 Serve() 方法以及我们的端口信息区实现阻塞等待,直到进程被杀死或者 Stop() 被调用
    	err = grpcServer.Serve(listener)
    	if err != nil {
    		log.Fatalf("grpcServer.Serve err: %v", err)
    	}
    }
    
    

    运行后,当输入数据验证失败后,会有以下错误返回

    2022/04/28 21:00:22 Call Route err: rpc error: code = InvalidArgument desc = invalid field SomeInteger: value '200' must be less than '100'
    
    

    image-20220428210046708

    五、其他类型验证规则设置

    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的数据验证,很简单也很方便。

  • 相关阅读:
    网页制作-表单元素2
    网页制作-表单元素
    网页制作_表格
    网页制作常用标签
    IT新起之秀
    Android Studio 快捷键
    android github
    手机抓包 http tcp udp?
    Ubuntu16.04 Caffe 安装步骤记录(超详尽)(转载)
    ubuntu16.04 在cuda9.0环境下编译安装opencv2.4.13.7
  • 原文地址:https://www.cnblogs.com/randysun/p/16273958.html
Copyright © 2020-2023  润新知