前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >ASP.NET Core解析Protobuf格式数据

ASP.NET Core解析Protobuf格式数据

作者头像
雪飞鸿
发布2022-09-20 19:49:54
1.2K0
发布2022-09-20 19:49:54
举报
文章被收录于专栏:me的随笔

ASP.NET Core通过IInputFormatter来解析输入的数据,并进行模型绑定(Model Binding);通过IOutputFormatter来解析输出的数据,来格式化响应(format response)。

两个Nuget包

谷歌提供了Google.Protobuf包用于解析Protocol Buffers数据,包括和json格式互转;Grpc.Tools包可根据proto文件在编译时生成对应的c#/c++文件。

ASP.NET Core解析Protocol Buffers

ASP.NET Core默认只支持对application/json的解析,要解析protobuf格式数据,需要引入nuget包:AspCoreProtobufFormatters,该包依赖Google.Protobuf包解析protobuf格式数据。

此外,通过Grpc.Tools生成的C#类型中,集合类型的属性是只读的,导致ASP.NET Core中默认的json formatter在进行模型绑定时,无法给集合类行属性赋值。AspCoreProtobufFormatters包当前版本(1.0.0版本)默认不支持application/json格式,可以通过扩展来支持:

代码语言:javascript
复制
internal static class HttpContentType
{
    public static class Application
    {
        public const string Json = "application/json";
    }
}
​
​
/// <summary>
/// 针对ContentType为<see cref="HttpContentType.Application.Json"/>类型数据的格式化器
/// </summary>
internal class ProtobufApplicationJsonFormatter : ProtobufJsonFormatter
{
    public ProtobufApplicationJsonFormatter() : base(HttpContentType.Application.Json) { }
​
    protected override (bool, byte[]) WriteBytes(IMessage message)
    => (true, Encoding.UTF8.GetBytes(ProtoModelTypeRegister.JsonFormatter.Format(message)));
}

将protobuf格式(IMessage类型)数据序列化为json格式时,需要将先注册相应的类型:

代码语言:javascript
复制
using Google.Protobuf;
using Google.Protobuf.Reflection;
​
namespace Models
{
    public static class ProtoModelTypeRegister
    {
        public static readonly JsonFormatter JsonFormatter;
​
        static ProtoModelTypeRegister()
        {
            var messageTypes = typeof(ProtoModelTypeRegister).Assembly.GetTypes()
                .Where(t => t.IsAbstract == false)
                .Where(t => t.IsAssignableTo(typeof(IMessage)));
​
            var descriptorList = new List<MessageDescriptor>();
            foreach (var msgType in messageTypes)
            {
                var descriptorProperty = msgType.GetProperty("Descriptor");
                if (descriptorProperty == null)
                {
                    continue;
                }
​
                if (descriptorProperty.GetValue(msgType) is MessageDescriptor messageDescriptor)
                {
                    descriptorList.Add(messageDescriptor);
                }
            }
​
            var typeRegistry = TypeRegistry.FromMessages(descriptorList);
            JsonFormatter = new JsonFormatter(new JsonFormatter.Settings(true, typeRegistry));
        }
    }
}

在ASP.NET Core中添加引用:

代码语言:javascript
复制
builder.Services.AddControllers(opt =>
{
    opt.AddProtobufFormatters(new IContentReader[] { new ProtobufBinFormatter(), new ProtobufApplicationJsonFormatter() },
                              new IContentWriter[] { new ProtobufBinFormatter(), new ProtobufApplicationJsonFormatter() });
});

注意,这里添加formatter时,Protobuf formatter在前,json formatter在后,所以会优先选用protobuf formatter来格式化数据。如果想要返回json格式数据,可以根据内容协商机制在Accept头字段中指定application/json。对于不支持内容协商的场景,可以通过自定义一个过滤器来实现:

代码语言:javascript
复制
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
internal class EnableJsonResponseFilterAttribute : ResultFilterAttribute
{
    public override void OnResultExecuting(ResultExecutingContext context)
    {
        if (context.HttpContext.Request.AcceptJson())
        {
            // 执行response format之前将response content-type设为json格式
            context.HttpContext.Response.ContentType = context.HttpContext.Request.ContentType!;
        }
    }
}
​
internal static class HttpExtensions
{
    /// <summary>
    /// 判断当前HTTP请求的Content-Type中是否包含 <see cref="HttpContentType.Application.Json"/>
    /// </summary>
    public static bool ContentTypeIsJson(this HttpRequest request)
    {
        foreach (var contentType in request.Headers.ContentType)
        {
            if (contentType.Contains(HttpContentType.Application.Json, StringComparison.OrdinalIgnoreCase))
            {
                return true;
            }
        }
​
        return false;
    }
​
    /// <summary>
    /// 判断当前HTTP请求是否接受Json格式的返回数据,目前只通过Content-Type来判断,忽略Accept
    /// </summary>
    public static bool AcceptJson(this HttpRequest request)
    {
        foreach (var accept in request.Headers.Accept)
        {
            if (accept.Contains(HttpContentType.Application.Json, StringComparison.OrdinalIgnoreCase))
            {
                return true;
            }
        }
​
        return ContentTypeIsJson(request);
    }
}
 

推荐阅读

Protocol Buffers

Custom formatters in ASP.NET Core Web API

Format response data in ASP.NET Core Web API

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-08-08,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 两个Nuget包
  • ASP.NET Core解析Protocol Buffers
  • 推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档