【问题标题】:How to make Protobuf 3 fields mandatory?如何强制 Protobuf 3 字段?
【发布时间】:2020-05-23 10:32:38
【问题描述】:

我正在使用 GRPC/proto-buffers 在 GoLang 中编写我的第一个 API 端点。我对 GoLang 比较陌生。 以下是我为测试用例编写的文件

package my_package

import (
    "context"
    "testing"

    "github.com/stretchr/testify/require"

    "google.golang.org/protobuf/types/known/structpb"
    "github.com/MyTeam/myproject/cmd/eventstream/setup"
    v1handler "github.com/MyTeam/myproject/internal/handlers/myproject/v1"
    v1interface "github.com/MyTeam/myproject/proto/.gen/go/myteam/myproject/v1"
)

func TestEndpoint(t *testing.T) {
    conf := &setup.Config{}

    // Initialize our API handlers
    myhandler := v1handler.New(&v1handler.Config{})

    t.Run("Success", func(t *testing.T) {
        res, err := myhandler.Endpoint(context.Background(), &v1interface.EndpointRequest{
            A: "S",
            B: &structpb.Struct{
                Fields: map[string]*structpb.Value{
                    "T": &structpb.Value{
                        Kind: &structpb.Value_StringValue{
                            StringValue: "U",
                        },
                    },
                    "V": &structpb.Value{
                        Kind: &structpb.Value_StringValue{
                            StringValue: "W",
                        },
                    },
                },
            },
            C: &timestamppb.Timestamp{Seconds: 1590179525, Nanos: 0},
        })
        require.Nil(t, err)

        // Assert we got what we want.
        require.Equal(t, "Ok", res.Text)
    })


}

这是EndpointRequest 对象在上面包含的v1.go 文件中的定义方式:

// An v1 interface Endpoint Request object.
message EndpointRequest {

  // a is something.
  string a = 1 [(validate.rules).string.min_len = 1];

  // b can be a complex object.
  google.protobuf.Struct b = 2;

  // c is a timestamp.
  google.protobuf.Timestamp c = 3;

}

上面的测试用例似乎工作正常。

我设置了有效地使参数a 强制的验证规则,因为它要求a 是至少一个字符串。因此,如果您省略 a,端点将返回 400。

但现在我想确保如果省略 cb,端点返回 400。我怎样才能做到这一点?在 Protobufs 3 中,他们去掉了 required 关键字。那么如何检查是否传入了非字符串参数并做出相应的反应呢?

【问题讨论】:

    标签: go protocol-buffers grpc-go


    【解决方案1】:

    简短版:你不能。

    required 被删除主要是因为它使更改向后不兼容。尝试使用验证选项重新实现它并不那么激烈(更改更容易),但正如您所见,会遇到一些缺点。

    相反,将验证排除在原型定义之外,并将其移至应用程序本身。任何时候你收到一条消息,你都应该检查它的内容(当required 是一个东西时也是如此)。由 options 或 required 提供的简单验证就足够的情况很少见。

    【讨论】:

    • 但是在我的应用程序本身中,如何检测bc 是否被传入?
    • 访问器(例如:GetB())将返回一个*Struct。消息中未指定时,返回值为 nil。见Go proto3 details。标量是个问题,因为它们不是指针而是普通类型,因此您无法区分 int 未设置与 0 之间的区别(有关详细信息和解决方法,请参阅 github issue)。
    • 哪些是标量,哪些不是?字符串、整数、时间戳、结构?
    • 上一条评论的链接中有一个列表,还有一个指向完整scalar types tables的链接。
    【解决方案2】:

    proto3 中删除了必填字段。 Here is github issue 您可以在其中阅读详细说明为什么这样做。摘录如下:

    我们在 proto3 中删除了必填字段,因为通常认为必填字段是有害的并且违反了 protobuf 的兼容性语义。使用 protobuf 的整个想法是,它允许您从协议定义中添加/删除字段,同时仍然与较新/较旧的二进制文件完全向前/向后兼容。必填字段打破了这一点。您永远无法安全地将必填字段添加到 .proto 定义中,也无法安全删除现有的必填字段,因为这两个操作都会破坏线路兼容性

    IMO,这是一个值得商榷的决定,显然我并不孤单,谁在这么想。最终决定应该留给开发人员。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-07-26
      • 1970-01-01
      • 2020-03-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-06-11
      相关资源
      最近更新 更多