c#:webapi项目和grpc项目中序列化问题

环境:

  • asp.net core3.1

一、先说下总结:

如果想让一个实体模型同时应用在webapi(使用newtonsoft.json序列化)和grpc环境中,那么可以参照如下形式:

[JsonObject(MemberSerialization.OptOut)]
[DataContract]
public class PageReq
{
    [DataMember(Order = 1)]
    public int PageIndex { get; set; }

    [DataMember(Order = 2)]
    public int PageSize { get; set; }
}

二、问题剖析

一般web框架都是用NewtonsoftJson序列化库,在asp.netcore中引用如下:

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers().AddNewtonsoftJson(options =>
    {
        //忽略循环引用
        options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
        //使用驼峰 首字母小写
        options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
        //设置时间格式
        options.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss";
        //将枚举序列化为字符串
        //options.SerializerSettings.Converters.Add(new StringEnumConverter());
    });
}

这么用一般没什么问题,然而在grpc中的序列化是需要借助[DataContract][DataMember]的,所以定义一个公共dto模型的时候就不得不考虑冲突的问题,
比如说分页请求模型:

public class PageReq
{
    public int PageIndex { get; set; }
    public int PageSize { get; set; }
}

public class GetUserListReq : PageReq
{
    public string Name { get; set; }
}

这个模型在web开发中是没有问题的,但是到了Grpc中就出现了问题:因为PageReq未标记为[DataContract],[DataMember] 所以不会被序列化

于是,在PageReq上增加标记如下:

public class PageReq
{
    [DataMember(Order = 1)]
    public int PageIndex { get; set; }

    [DataMember(Order = 2)]
    public int PageSize { get; set; }
}

public class GetUserListReq : PageReq
{
    [DataMember(Order = 3)]
    public string Name { get; set; }
}

当打上标记后,针对grpc的数据传输是没有问题了,但当这个模型应用在webapi开发中时,我们发现所有继承自 PageReq的模型属性都必须打上DataMember标记,否则不被序列化和反序列化,如下所示:

/*模型*/
[DataContract]
public class PageReq
{
    [DataMember(Order = 1)]
    public int PageIndex { get; set; }

    [DataMember(Order = 2)]
    public int PageSize { get; set; }
}

public class GetUserListReq : PageReq
{
    public string Name { get; set; }
}

/*startup.cs引入newtonsoft*/
public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers(config =>
    {
        config.Filters.Add<GlobalExceptionFilter>();
    }).AddNewtonsoftJson(options =>
    {
        //忽略循环引用
        options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
        //使用驼峰 首字母小写
        options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
        //设置时间格式
        options.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss";
        //将枚举序列化为字符串
        options.SerializerSettings.Converters.Add(new StringEnumConverter());
    });
}

/*Controller定义*/
[Route("api/[controller]/[action]")]
public class DemoController : ControllerBase
{
    [HttpPost]
    public string TestJson([FromBody] GetUserListReq req)
    {
        if (string.IsNullOrWhiteSpace(req.Name)) req.Name = "xxxx";
        var res = Newtonsoft.Json.JsonConvert.SerializeObject(req);
        return $"res={res}\r\nname={req.Name},pageIndex={req.PageIndex},pageSize={req.PageSize}";
    }
}

postman测试情况:
在这里插入图片描述

这就尴尬了,先说下原因:

默认NewtonsoftJson序列化的规则是对所有公共属性都进行序列化,除非手动指定忽略哪些属性,如下:
在这里插入图片描述
而当我们在标记为[DataContract]特性后,就把序列化模式从OptOut改为 OptIn,在OptIn模式下,NewtonsoftJson只会序列化标记为JsonPropertyDataMember特性的属性,其他的则忽略。

让我们来看一下,NewtonsoftJson的说明:
在这里插入图片描述

知道了其中的原因,那么将公共模型PageReq定义如下:

[JsonObject(MemberSerialization.OptOut)]
[DataContract]
public class PageReq
{
    [DataMember(Order = 1)]
    public int PageIndex { get; set; }

    [DataMember(Order = 2)]
    public int PageSize { get; set; }
}

public class GetUserListReq : PageReq
{
    public string Name { get; set; }
}

这样,再看一下postman的情况:
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

jackletter

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值