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,使用哪种格式通过网络返回数据?的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 PrefixStyle 和 ProtoBuf-net 获得长度数?