禾卡 發表於 2020-4-23 15:23:00

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

<h3 id="前言">前言</h3>
<p>上篇介绍了go-grpc-middleware的<code>grpc_zap</code>、<code>grpc_auth</code>和<code>grpc_recovery</code>使用,本篇将介绍<code>grpc_validator</code>,它可以对gRPC数据的输入和输出进行验证。</p>
<h3 id="创建proto文件添加验证规则">创建proto文件,添加验证规则</h3>
<p>这里使用第三方插件go-proto-validators自动生成验证规则。</p>
<p><code>go get github.com/mwitkow/go-proto-validators</code></p>
<p>1.新建simple.proto文件</p>
<pre><code class="language-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: "^{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){};
}
</code></pre>
<p>代码<code>import "github.com/mwitkow/go-proto-validators/validator.proto"</code>,文件<code>validator.proto</code>需要<code>import "google/protobuf/descriptor.proto";</code>包,不然会报错。</p>
<p><code>google/protobuf</code>地址:https://github.com/protocolbuffers/protobuf/tree/master/src/google/protobuf/descriptor.proto</p>
<p>把<code>src</code>文件夹中的<code>protobuf</code>目录下载到GOPATH目录下。</p>
<p>2.编译simple.proto文件</p>
<p><code>go get github.com/mwitkow/go-proto-validators/protoc-gen-govalidators</code></p>
<p>指令编译:<code>protoc --govalidators_out=. --go_out=plugins=grpc:./ ./simple.proto</code></p>
<blockquote>
<p>或者使用<code>VSCode-proto3</code>插件,第一篇有介绍。只需要添加<code>"--govalidators_out=."</code>即可。</p>
</blockquote>
<pre><code>    // vscode-proto3插件配置
    "protoc": {
      // protoc.exe所在目录
      "path": "C:\\Go\\bin\\protoc.exe",
      // 保存时自动编译
      "compile_on_save": true,
      "options": [
            // go编译输出指令
            "--go_out=plugins=grpc:.",
            "--govalidators_out=."
      ]
    },
</code></pre>
<p>编译完成后,自动生成<code>simple.pb.go</code>和<code>simple.validator.pb.go</code>文件,<code>simple.pb.go</code>文件不再介绍,我们看下<code>simple.validator.pb.go</code>文件。</p>
<pre><code class="language-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 &gt; 0) {
                return github_com_mwitkow_go_proto_validators.FieldError("SomeInteger", fmt.Errorf(`value '%v' must be greater than '0'`, this.SomeInteger))
        }
        if !(this.SomeInteger &lt; 100) {
                return github_com_mwitkow_go_proto_validators.FieldError("SomeInteger", fmt.Errorf(`value '%v' must be less than '100'`, this.SomeInteger))
        }
        if !(this.SomeFloat &gt;= 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 &lt;= 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(`^{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 "^{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
}
</code></pre>
<p>里面自动生成了<code>message</code>中属性的验证规则。</p>
<h3 id="把grpc_validator验证拦截器添加到服务端">把<code>grpc_validator</code>验证拦截器添加到服务端</h3>
<pre><code class="language-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()),
                )),
        )
</code></pre>
<p>运行后,当输入数据验证失败后,会有以下错误返回</p>
<pre><code class="language-powershell">Call Route err: rpc error: code = InvalidArgument desc = invalid field SomeInteger: value '101' must be less than '100'
</code></pre>
<h3 id="其他类型验证规则设置">其他类型验证规则设置</h3>
<p><code>enum</code>验证</p>
<pre><code class="language-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;
}
</code></pre>
<p><code>UUID</code>验证</p>
<pre><code class="language-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}];
}
</code></pre>
<h3 id="总结">总结</h3>
<p><code>go-grpc-middleware</code>中<code>grpc_validator</code>集成<code>go-proto-validators</code>,我们只需要在编写proto时设好验证规则,并把<code>grpc_validator</code>添加到gRPC服务端,就能完成gRPC的数据验证,很简单也很方便。</p>
<p>教程源码地址:https://github.com/Bingjian-Zhu/go-grpc-example</p>


</div>
<div id="MySignature" role="contentinfo">
    看完之后若觉得对自己有帮助,恳请点赞或评论。这是对我最大的鼓励!<br><br>
来源:https://www.cnblogs.com/FireworksEasyCool/p/12761033.html
頁: [1]
查看完整版本: Go gRPC进阶-proto数据验证(九)