环境:
- window10
- asp.net core 3.1
一般web框架都是用Newtonsoft.Json序列化库,在asp.net core中引用如下:
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上增加标记如下:
[DataContract]
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的数据传输是没有问题了,但当这个模型应用在web开发中时,我们发现所有继承自 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*/
//...此处省略
/*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测试情况:
这就尴尬了,先说下原因:
默认Newtonsoft.Json
序列化的规则是对所有公共属性都进行序列化,除非手动指定忽略哪些属性,如下:
而当我们在标记为[DataContract]
特性后,就把序列化模式从OptOut
改为OptIn
,在OptIn
模式下,Newtonsoft.Json
只会序列化标记为JsonProperty
或DataMember
特性的属性,其他的则忽略。
让我们来看一下,Newtonsoft.Json的说明:
知道了其中的原因,那么将公共模型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的情况: