简体   繁体   English

google.protobuf.Any 带有 grpc 的字段始终为零

[英]google.protobuf.Any field with grpc is always nil

I am using gRPC with golang.我在 golang 中使用 gRPC。 I have a very simple proto definition and a gRPC service.我有一个非常简单的 proto 定义和一个 gRPC 服务。 The proto definition has a field in Endorsement of type google/protobuf/any .原型定义在类型为google/protobuf/any 的背书中有一个字段。 gRPC service is unable to map this field to input value and it's always getting initialised to nil gRPC 服务无法将此字段映射到输入值,并且始终初始化为零

proto definition :原型定义

syntax = "proto3";

option go_package = "service";
option java_multiple_files = true;
option java_package = "io.grpc.consensus";

import "google/protobuf/any.proto";

package service;

service MyService {
  rpc Verify (Payload) returns (Response) {}
}

message Response {
  string policyId =1;
  string txnId =2;
}

message Endorsement {
  string endorserId=1;
  // This is being initialise to nil by gRPC
  google.protobuf.Any data = 2;
  string signature=3;
  bool isVerified=4;
}


message Payload {
  string policyId =1;
  string txnId =2;
  repeated Endorsement endorsements=3;
}

Using this, a simple gRPC service is implemented:使用它,实现了一个简单的 gRPC 服务:

package service

import (
    "log"

    "golang.org/x/net/context"
)

type ServiceServerImpl struct {
}

func NewServiceServerImpl() *ServiceServerImpl {
    return &ServiceServerImpl{}
}

func (s *ServiceServerImpl) Verify(ctx context.Context, txnPayload *Payload) (*Response, error) {
    log.Printf("Got verification request: %s", txnPayload.TxnId)
    for _, endorsement := range txnPayload.Endorsements {
        j, err := endorsement.Data.UnmarshalNew()
        if err != nil {
            log.Print("Error while unmarshaling the endorsement")
        }
        if j==nil {
       //This gets printed as payload's endorsement data is always null for google/protobuf/any type
            log.Print("Data is null for endorsement")
        }
    }
    return &Response{TxnId: txnPayload.TxnId,  PolicyId: txnPayload.PolicyId}, nil
}

Input Data:输入数据:

{
  "policyId": "9dd97b1e-b76f-4c49-b067-22143c954e75",
  "txnId": "231-4dc0-8e54-58231df6f0ce",
  "endorsements": [
    {
      "endorserId": "67e1dfbd-1716-4d91-94ec-83dde64e4b80",
      "data": {
        "type": "issueTx",
        "userId": 1,
        "transaction": {
            "amount": 10123.50
        }
    },
      "signature": "MEUCIBkooxG2uFZeSEeaf5Xh5hWLxcKGMxCZzfnPshOh22y2AiEAwVLAaGhccUv8UhgC291qNWtxrGawX2pPsI7UUA/7QLM=",
      "isVerified": false
    }
  ]
}

Client:客户:

type Data struct {
    Type        string      `json:"type"`
    UserId      int16       `json:"userId"`
    Transaction Transaction `json:"transaction"`
}

type Transaction struct {
    Amount float32 `json:"amount"`
}

data := &Data{Type: "Buffer", UserId: 1, Transaction: Transaction{Amount: 10123.50}}
    byteData, err := json.Marshal(data)
    if err != nil {
        log.Printf("Could not convert data input to bytes")
    }

    e := &any.Any{
        TypeUrl: "anything",
        Value:   byteData,
    }

    endosement := &Endorsement{EndorserId: "1", Signature: "MEUCIBkooxG2uFZeSEeaf5Xh5hWLxcKGMxCZzfnPshOh22y2AiEAwVLAaGhccUv8UhgC291qNWtxrGawX2pPsI7UUA/7QLM=", Data: e, IsVerified: false}
    var endosements = make([]*Endorsement, 1)
    endosements[0] = endosement
    t := &Payload{TxnId: "123", PolicyId: "456", Endorsements: endosements}

    response, err := c.Verify(context.Background(), t)
    if err != nil {
        log.Fatalf("Error when calling SayHello: %s", err)
    }

How should google/protobuf/any type be Unmarshal for unknown types?对于未知类型, google/protobuf/any type 应该如何解组?

m, err := e.Data.UnmarshalNew()
    if err != nil {
        log.Print("Error while unmarshaling the endorsement")
    }

throws an error: s:"not found"抛出错误:s:“未找到”

As you know google.protobuf.Any is messageType, so when you didn't set any value for it, It will be nil.如您所知google.protobuf.Any是 messageType,因此当您没有为其设置任何值时,它将为零。

You must unmarshal your data with your struct, see code below from example of protobuf您必须使用结构解组数据,请参阅下面的protobuf示例中的代码

      // marshal any
      foo := &pb.Foo{...}
      any, err := anypb.New(foo)
      if err != nil {
        ...
      }
      // unmarshal any 
      foo := &pb.Foo{}
      if err := any.UnmarshalTo(foo); err != nil {
        ...
      }

Or I think you can use it with pointer interface (&interface{}) like this :或者我认为您可以将它与指针接口(&interface{})一起使用,如下所示:

      d := &interface{}{}
      if err := endorsement.Data.UnmarshalTo(d); err != nil {
        ...
      }

From the package 1 documentation Unmarshaling an Any :从包1文档解组 Any

UnmarshalNew uses the global type registry to resolve the message type and construct a new instance of that message to unmarshal into. UnmarshalNew使用全局类型注册表来解析消息类型并构造该消息的新实例以进行解组。 In order for a message type to appear in the global registry, the Go type representing that protobuf message type must be linked into the Go binary.为了使消息类型出现在全局注册表中,必须将表示该 protobuf 消息类型的 Go 类型链接到 Go 二进制文件中。 For messages generated by protoc-gen-go, this is achieved through an import of the generated Go package representing a .proto file .对于 protoc-gen-go 生成的消息,这是通过导入表示 .proto 文件的生成的 Go 包来实现的。

The type registry is protoregistry.GlobalTypes .类型注册表是protoregistry.GlobalTypes The type lookup is done using the Any.TypeUrl field, which is set to the concrete type's url by the gRPC client when marshalling the original message.类型查找是使用Any.TypeUrl字段完成的,该字段在编组原始消息时由 gRPC 客户端设置为具体类型的 url。

The confusing detail about Any is that it can be any protobuf message, but that protobuf message must be defined somewhere . Any令人困惑的细节是它可以是任何protobuf 消息,但该 protobuf 消息必须在某处定义

Your .proto file has no message definition that matches the data object in your input.您的.proto文件没有与输入中的data对象匹配的消息定义。 It could be that this Data message is defined somewhere else (not in your own proto files), but irrespective of that you have to import the Go package where the generated message is.可能是这个Data消息是在其他地方定义的(不是在你自己的 proto 文件中),但无论如何你必须导入生成消息所在的 Go 包。

Otherwise if the input doesn't come from an already defined proto message, you can add a message definition to your proto yourself, then use UnmarshalTo :否则,如果输入不是来自已定义的 proto 消息,您可以自己向 proto 添加消息定义,然后使用UnmarshalTo

// proto file
message Data {
    string type = 1;
    int user_id = 2;
    Transaction transaction = 3;
}

message Transaction {
    float amount = 1;
}

And then:然后:

    for _, endorsement := range txnPayload.Endorsements {
        data := generated.Data{}
        err := endorsement.Data.UnmarshalTo(&data)
        if err != nil {
            log.Print("Error while unmarshaling the endorsement")
        }
    }

If you only need an arbitrary sequence of bytes, ie a truly unknown type, then use the proto type bytes and treat it as a JSON payload.如果您只需要任意字节序列,即真正未知的类型,则使用原型bytes并将其视为 JSON 有效负载。

Model it as a Go struct:将其建模为 Go 结构体:

type Data struct {
    Type     string `json:"type"`
    UserID   int    `json:"userId"`
    Transaction struct{
        Amount float64 `json:"amount"`
    } `json:"transaction"`

}

Or as a map[string]interface{} if clients could be sending literally anything.或者作为map[string]interface{}如果客户端可以发送任何东西。

Then in your handler func:然后在您的处理程序函数中:

    for _, endorsement := range txnPayload.Endorsements {
        data := Data{} // or `map[string]interface{}`
        err := json.Unmarshal(endorsement.Data, &data)
        if err != nil {
            log.Print("Error while unmarshaling the endorsement")
        }
    }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM