Protobuf-net 和 WebApi,使用哪种格式通过网络返回数据?

Posted

技术标签:

【中文标题】Protobuf-net 和 WebApi,使用哪种格式通过网络返回数据?【英文标题】:Protobuf-net and WebApi, which format to use to return data over the wire? 【发布时间】:2020-06-07 17:08:08 【问题描述】:

我有一个 .Net Core 3.1 WebAPI:

    使用以下方法序列化我的 obj:ProtoBuf.Serializer.Serialize(stream, 我的对象); 返回:Convert.ToBase64String(stream.GetBuffer(), 0, (int)stream.Length);

和调用 WebAPI 的 Blazor WASM 应用程序和

    从线路获取数据:MemoryStream 流 = 新 MemoryStream(Convert.FromBase64String(等待 response.Content.ReadAsStringAsync())); 然后将其反序列化回我的 obj:myobj = ProtoBuf.Serializer.Deserialize(stream);

一切正常,但我怀疑这可以优化并避免一些转换为 byte[] / base4 字符串并返回。

这是通过网络传输 protobuf 数据的正确方法还是可以以更好的方式完成?

【问题讨论】:

有趣的问题。我真的不知道答案,但希望... 您可以通过 http 发送二进制数据 (byte[]) - 没有 base64 编码,我从来没有遇到过这样的问题。通过线路传输的字节数更少,并且没有字符串来回转换(base64)。 @FrankNielsen 你能分享一些 API 和客户端的代码吗? 【参考方案1】:

如何通过 http 线路发送原始字节:

var binaryContent = new ByteArrayContent(new byte[]  1, 2, 3 );
binaryContent.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
content.Add(binaryContent, "content");

var result = await client.PostAsync("api/bytes/incoming", content);

诀窍是接收者必须知道“ContentType: application/octet-stream”,这对于大多数 Web 服务器来说不是默认设置。所以服务器端需要多花点功夫:

public class RawRequestBodyFormatter : InputFormatter

    public RawRequestBodyFormatter()
    
        SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/plain"));
        SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/octet-stream"));
    


    /// <summary>
    /// Allow text/plain, application/octet-stream and no content type to
    /// be processed
    /// </summary>
    /// <param name="context"></param>
    /// <returns></returns>
    public override Boolean CanRead(InputFormatterContext context)
    
        if (context == null) throw new ArgumentNullException(nameof(context));

        var contentType = context.HttpContext.Request.ContentType;
        if (string.IsNullOrEmpty(contentType) || contentType == "text/plain" ||
            contentType == "application/octet-stream")
            return true;

        return false;
    

    /// <summary>
    /// Handle text/plain or no content type for string results
    /// Handle application/octet-stream for byte[] results
    /// </summary>
    /// <param name="context"></param>
    /// <returns></returns>
    public override async Task<InputFormatterResult> ReadRequestBodyAsync(InputFormatterContext context)
    
        var request = context.HttpContext.Request;
        var contentType = context.HttpContext.Request.ContentType;


        if (string.IsNullOrEmpty(contentType) || contentType == "text/plain")
        
            using (var reader = new StreamReader(request.Body))
            
                var content = await reader.ReadToEndAsync();
                return await InputFormatterResult.SuccessAsync(content);
            
        
        if (contentType == "application/octet-stream")
        
            using (var ms = new MemoryStream(2048))
            
                await request.Body.CopyToAsync(ms);
                var content = ms.ToArray();
                return await InputFormatterResult.SuccessAsync(content);
            
        

        return await InputFormatterResult.FailureAsync();
    

在 Startup.cs

public void ConfigureServices(IServiceCollection services)

    services.AddMvc(o => o.InputFormatters.Insert(0, new RawRequestBodyFormatter()));

和控制器

[HttpPost]
[Route("api/bytes/incoming")]
public byte[] RawBytesFormatter([FromBody] byte[] rawData)

    return rawData;
 

我从 Rick Strahl 的优秀博文中获取了服务器端 sn-ps:https://weblog.west-wind.com/posts/2017/Sep/14/Accepting-Raw-Request-Body-Content-in-ASPNET-Core-API-Controllers?Page=2

向他致敬 :)

【讨论】:

【参考方案2】:

我发现WebAPI可以直接返回ProtoBuf.Serializer.Serialize写入的流。这里的重点是不要处理流(让它对 GC 进行处理)。

客户端可以ProtoBuf.Serializer.Deserialize response.Content.ReadAsStreamAsync()。

但是

我坚持使用 Base64string,以便服务器对内容进行 gzip 压缩,而不是流。

【讨论】:

以上是关于Protobuf-net 和 WebApi,使用哪种格式通过网络返回数据?的主要内容,如果未能解决你的问题,请参考以下文章

发布 webapi 后选择哪一个

[转]序列化悍将Protobuf-Net,入门动手实录

在 Azure 中托管 WebApi 的推荐方式

如何使用 PrefixStyle 和 ProtoBuf-net 获得长度数?

带有 ServiceStack 和紧凑框架的 Protobuf-net

WCF Rest 服务和 protobuf-net