【问题标题】:.Net WebAPI returning Protocol Buffer: Protocol message contained a tag with an invalid wire type.Net WebAPI 返回协议缓冲区:协议消息包含带有无效线路类型的标签
【发布时间】:2021-09-22 20:46:02
【问题描述】:

我有一个 *.proto 文件,它定义了两条消息:MyRequest 和 MyResponse。这是在通用 .NET Standard 2.0 库中定义的,并使用以下 NuGet 包:

    <PackageReference Include="Google.Protobuf" Version="3.18.0" />
    <PackageReference Include="Grpc" Version="2.40.0" />
    <PackageReference Include="Grpc.Tools" Version="2.40.0">

服务器

我有一个 .NET Framework (4.8) WebAPI 和以下操作方法:

[HttpPost]
public async Task<IHttpActionResult> FetchSomeData()
{
    using (var ms = new MemoryStream(2048))
    {
        await content.CopyToAsync(ms).ConfigureAwait(false);
        byte[] bytes = ms.ToArray();
        MyRequest request = MyRequest.Parser.ParseFrom(bytes);

        MyResponse response = await SomeMethodAsync(request).ConfigureAwait(false);
        byte[] responseByteArray = response.ToByteArray();
        return this.Ok(responseByteArray);
    }
}

所以这成功接收到一个MyRequest 对象发送通过 HttpPost,并根据该对象中的数据,生成一个MyResponse 对象并返回。

客户

我有一个使用 HttpClient 使用此服务的 .Net 5 客户端:

// Prepare Request
MyRequest webRequest = new() { ... };
byte[] byteArray = webRequest.ToByteArray();
ByteArrayContent byteContent = new(byteArray);

// Send data
HttpClient client = this.HttpClientFactory.CreateClient("Blah");
HttpResponseMessage webResponse = await client.PostAsync(new Uri("...", UriKind.Relative), byteContent).ConfigureAwait(false);

// Read response
HttpContent content = webResponse.Content;
byte[] webMethodResponseAsByteArray = await content.ReadAsByteArrayAsync().ConfigureAwait(false);
MyResponse webMethodResponse = MyResponse.Parser.ParseFrom(webMethodResponseAsByteArray);

HttpClient "Blah" 仅配置有基本 URL 和安全令牌。

但是....当我在最后一行调用 ParseFrom 时,出现以下异常:

Google.Protobuf.InvalidProtocolBufferException
  HResult=0x80131620
  Message=Protocol message contained a tag with an invalid wire type.
  Source=Google.Protobuf
  StackTrace:
   at Google.Protobuf.UnknownFieldSet.MergeFieldFrom(ParseContext& ctx)
   at Google.Protobuf.UnknownFieldSet.MergeFieldFrom(UnknownFieldSet unknownFields, ParseContext& ctx)
   at namespace.MyResponse.pb::Google.Protobuf.IBufferMessage.InternalMergeFrom(ParseContext& input) in ....

不知道如何解决这个问题...

【问题讨论】:

  • 你能发布失败的有效载荷的十六进制或base-64(即webMethodResponseAsByteArray)吗?从中我们可能可以了解更多关于发生的事情 - 特别是,我们可以通过protogen.marcgravell.com/decode 运行它以查看它是否有效,以及它的样子;然而!如果 http/2 是一个选项,感觉整个事情可以更方便地成为一个 gRPC 端点。 (我几乎可以肯定能帮你解决这个问题,但时区......我可能会在接下来的 8 小时内离线)
  • 嗨,马克。如果我可以使用 gRPC 那就太好了,但服务器是 .NET Framework (4.8),我的理解是只有 .NET 风格的核心与 gRPC 兼容。
  • 有趣的是,服务器 responseByteArray 的大小为 1912,而客户端 webMethodResponseAsByteArray 的大小为 2554。
  • @MarcGravell 我捕获了两个十六进制字符串(服务器 = 大小 1912:“08-01-1A-44-......-72-02-45-41”)和(客户端= size 2552: "22-43-41-45-......-51-3D-3D-22") 并粘贴到您的 [decode](protogen.marcgravell.com/decode) 工具中。服务器一号已解码 - 客户端一号未返回任何内容。
  • re gRPC:假设 gRPC 仅是 .NET Core 是不正确的;它比这更复杂;基本上,有两个独立的传输层——一个由 Google(非托管,通过 P/Invoke 到 CHTTP2)和一个由 Microsoft(完全托管)提供; managed 选项仅适用于 .NET Core;但是,unmanaged 选项(Grpc.Core 包)适用于 .NET Framework。 库层(Grpc 包)可以很好地与其中任何一个一起工作——所有的变化都是你创建客户端通道和服务器实例的方式。所以:gRPC 绝对是你的一个选择。所有 Google 文档都将显示 Grpc.Core。

标签: serialization protocol-buffers protocol-buffers-3


【解决方案1】:

我通过更改 .NET Framework (4.8) Server 的方法解决了我面临的问题:

来自

[HttpPost]
public async Task<IHttpActionResult> FetchSomeData()
{
    using (var ms = new MemoryStream(2048))
    {
        await content.CopyToAsync(ms).ConfigureAwait(false);
        byte[] bytes = ms.ToArray();
        MyRequest request = MyRequest.Parser.ParseFrom(bytes);

        MyResponse response = await SomeMethodAsync(request).ConfigureAwait(false);
        byte[] responseByteArray = response.ToByteArray();
        return this.Ok(responseByteArray);
    }
}

到

[HttpPost]
public async Task<HttpResponseMessage FetchSomeData()
{
    using (var ms = new MemoryStream(2048))
    {
        await content.CopyToAsync(ms).ConfigureAwait(false);
        byte[] bytes = ms.ToArray();
        MyRequest request = MyRequest.Parser.ParseFrom(bytes);

        MyResponse response = await SomeMethodAsync(request).ConfigureAwait(false);
        byte[] responseByteArray = response.ToByteArray();
        var result = new HttpResponseMessage(HttpStatusCode.OK)
        {
           Content = new ByteArrayContent(responseByteArray),
        };
        return result;
    }
}

但是,还要考虑 @MarcGravell 提到的 gRPC 方法(请参阅 cmets/conversation)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-06-27
    • 1970-01-01
    • 2015-10-29
    • 2014-12-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多