什么是?NET Core?
随着Xamarin和微软推出。NET基金会2014年,微软开放了源代码。NET框架于2014年11月发布。在的统一规划下。 NET开源基金会。NET Core诞生了。也就是说。NET核心框架是参考。NET框架,而Mono是一个开源和跨平台的实现。NET框架。
本文主要介绍了多态数据绑定的相关内容。NET Core WebApi,并分享给大家参考学习。下面就不多说了,我们来看看详细的介绍。
什么是多态数据绑定?
众所周知,ASP.NET核心Web API中的数据绑定机制负责绑定请求参数。通常,大多数数据绑定可以在默认的数据绑定器中正常执行,但是也会有一些不被支持的情况,比如多态数据绑定。所谓多态数据绑定,就是请求参数是子类对象的Json字符串,父类类型的变量是在动作中定义的。默认情况下,ASP.NET核心网API不支持多态数据绑定,这会导致数据丢失。
下图是一个示例
Person类是父类,Doctor类和Student类是Person类的派生类。属性“医院名称”在“医生”类别中,属性“学校名称”在“学生”类别中。
接下来,我们创建一个网络应用编程接口项目,并添加一个仁科控制器。
在PeopleController中,我们添加了一个Add api,并直接返回请求的数据来检查效果。
[Route(' API/people ')]公共类people Controller : Controller {[httpset][Route(')]公共listener Add([from body]listener people){ return people;}}这里我们使用Postman来请求这个api。请求的内容类型是application/json,请求的Body内容如下。
[{名字: 'Mike ',姓氏: 'Li'},{名字: 'Stephie ',姓氏: 'Wang ',学校名字: '第十五中学' },{名字:' Jacky ',姓氏:' Chen ',医院名字: '中心医院' }]请求返回内容
[ { 'FirstName': 'Mike ',' LastName': 'Li' },{ 'FirstName': 'Stephie ',' LastName': 'Wang' },{ 'FirstName': 'Jacky ',' LastName': 'Chen' }]返回的结果和我们想要的不太一样,Student持有的SchoolName属性和Doctor持有的HospitalName属性都丢失了。
现在我们启动项目调试模式,重用Postman请求一次,结果如下
“人员”集合中存储了三个“人员”类型的对象,但是预期的“学生”类型的对象和“医生”类型的对象没有出现。这表明。默认情况下,NET Core WebApi不支持多态数据绑定。如果父类类型变量用于接收数据,数据绑定将只实例化父类对象,而不是派生类对象,从而导致属性丢失。
定制JsonConverter实现多态数据绑定
JsonConverter是Json.NET的一个类,主要负责Json对象的序列化和反序列化。
首先,我们创建一个泛型类JsonCreationConverter,并继承JsonConverter类。代码如下:
公共抽象类jsoncreationconverter : JsonConverter { public override bool CanWrite { get { return false;} }受保护的抽象创建(类型对象类型,作业对象作业对象);公共覆盖布尔转换(类型对象类型){返回类型(T)10 .IsAssignableFrom(对象类型);}公共重写对象ReadJson(JsonReader读取器,类型对象类型,对象存在值,JsonSerializer序列化程序){ if (reader==null)引发新的argumentNullException(' reader ');如果(序列化程序==null)引发新的ArgumentNullException(“”序列化程序');如果(读者.TokenType==JsonToken .Null)返回null作业对象作业对象=作业对象。加载(阅读器);目标=创建(对象类型,作业对象);序列化程序。填充(工作项目.CreateReader(),目标);返回目标;} public override void WriteJson(JsonWriter writer,对象值,JsonSerializer序列化程序){抛出新的notimplementdexception();}}其中,我们加入了一个抽象方法创造,这个方法会负责根据数据字符串的内容,返回一个泛型类型对象,这里既可以返回一个当前泛型类型的对象,也可以返回一个当前泛型类型派生类的对象工作项目是Json .网中的数据字符串读取器,负责读取数据字符串中属性的值。
另外我们还复写了ReadJson方法,在ReadJson中我们会先调用创造方法获取一个当前泛型类对象或者当前泛型类的派生类对象(Json .网中默认的KeyValuePairConverter会直接实例化当前参数类型对象,这也就是默认不支持多态数据绑定的主要原因),序列化程序。人口普查方法的作用是将数据字符串的内容映射到目标对象(当前泛型类对象或者当前泛型类的派生类对象)的对应属性。
这里由于我们只需要读取Json,所以WriteJson的方法我们不需要实现,CanWrite属性我们也强制返回了假的。
第二步,我们创建一个PersonJsonConverter类,它继承了JsonCreationConverterPerson,其代码如下
公共类PersonJsonConverter : jsoncreationconverter person {受保护覆盖人员创建(类型对象类型,作业对象作业对象){如果(作业对象==空)抛出新的ArgumentNullException('作业对象');if (jObject['schoolName']!=null){ 0返回新的student();} else if(Jobject[' Hospital name ']!=null){ 0返回新的博士();} else {返回新的person();} }}在这个类中我们复写了创造方法,这里我们使用作业对象来获取数据字符串中拥有的属性。
如果字符串中包含学校名称属性,就返回一个新的学生对象如果字符串中包含医院名称属性,就返回一个新的医生对象否则,返回一个新人对象最后一步,我们在人类中使用特性标注人类使用PersonJsonConverter来进行转换数据序列化和反序列化。
[JsonConverter(PersonJsonConverter的类型))]公共类person { public string first name { get;设置;}公共字符串姓氏{ get设置;}}现在我们重新使用调试模式启动程序,然后使用邮递员请求当前美国石油学会(美国石油协会)
我们会发现各位,集合中已经正确绑定了的派生子类类型对象,最终邮递员上我们得到以下响应结果
[ { 'FirstName': 'Mike ',' LastName': 'Li' },{ 'SchoolName': '第十五中学,'名字' : '斯蒂芬','姓氏' : '王},{ ' HospitalName ' : '中心医院,'名字' : '杰克','姓氏' : '陈' }]至此多态数据绑定成功。
刨根问底
为什么添加了一个PersonJsonConverter类,多态绑定就实现了呢?
让我们来一起回顾一下手动音量调节核心以及Json .网的代码。
首先我们看一下MvcCoreMvcOptionsSetup设置设置代码
公共类mvccoremvcoptions set : iconfightoptions vcoptions { private readonly IHttpRequestStreamReaderFactory _ readerFactory;私有只读ILoggerFactory _ loggerFactory.公共无效配置(多副本选项){选项.ModelBinderProviders。添加(新的BinderTypeModelBinderProvider());选项模型绑定提供程序。添加(新服务modelbinderprovider());选项模型绑定提供程序。添加(新的BodyModelBinderProvider(选项. InputFormatters,_readerFactory,_loggerFactory,options));} .}MvcCoreMvcOptionsSetup类中的安装使成形方法设置了默认数据绑定使用供应者列表。
当一个美国石油学会(美国石油协会)参数被标记为[FromBody]时,BodyModelBinderProvider会实例化一个BodyModelBinder对象来处理这个参数并尝试进行数据绑定。
BodyModelBinder类中有一个BindModelAsync方法,从名字的字面意思上我们很清楚的知道这个方法就是用来绑定数据的。
公共异步任务绑定模型异步(模型绑定上下文绑定上下文){ if(绑定上下文==null)}引发新的ArgumentNullException(绑定上下文的名称));} ….var formatter=(IInputFormatter)null;for(var I=0;i _formatters .计数;i ) { if (_formatters[i]).CanRead(formatterContext)){ formatter=_ formatters[I];_logger?InputFormatterSelected(格式化程序,formatterContext);打破;} else { logger?inputformatterejected(_ formatters[I],formatterContext);} } ……尝试{ var result=等待格式化程序.ReadAsync(formatterContext);…… }在(异常为inputformaterexception | |应该处理异常(格式化程序))时捕获(异常异常异常){ bindingContext .ModelState。addmodelarry(modelBindingKey,异常,绑定上下文.模型元数据);}}在这个方法中它会尝试寻找一个匹配的输入格式化程序对象来绑定数据,由于这时候请求的内容类型是应用程序/json,所以这里会使用JsonInputFormatter对象来进行数据绑定。
下面我们看一下JsonInputFormatter类的部分关键代码
公共覆盖异步任务输入格式结果读取请求体异步(输入格式上下文,编码编码){ 0.使用(var streamReader=context .ReaderFactory(请求身体,编码)){使用(var jsonReader=new JsonTextReader(流读取器)){…对象模型;尝试{ model=jsonSerializer .反序列化(jsonReader,类型);}最后{ jsonSerializer .error-=错误处理程序;ReleaseJsonSerializer(jsonSerializer);} … } }}JsonInputFormatter类中的ReadRequestBodyAsync方法负责数据绑定,在该方法中使用了Json .网的JsonSerializer类的反序列化方法来进行反序列化,这说明手动音量调节核心的底层是直接使用Json .网来操作数据的。
JsonSerializer类的部分关键代码
公共对象反序列化(JsonReader读取器,类型objectType){ return反序列化内部(读取器,ObjectType);}内部虚拟对象反序列化内部(JsonReader读取器,Type objectType){……jsonserializerinternarreader序列化程序读取器=new jsonserializerinternarreader(this);对象值=serializerReader .反序列化(traceJsonReader?读取器、对象类型、检查附加内容);……返回值;}JsonSerializer会调用JsonSerializerInternalReader类的反序列化方法将数据字符串内容反序列化。
最终我们看一下JsonSerializerInternalReader中的部分关键代码
公共对象反序列化(JsonReader读取器,Type objectType,bool checkAdditionalContent){…JsonConverter converter=GetConverter(contract,null,null,null);if (reader。TokenType==JsonToken。没有!读者。ReadForType(合同,转换器!=null)){ 0.对象反序列化值;if(转换器!=空转换器。CanRead) {反序列化值=反序列化转换表(转换器,读取器,objectType,null);} else {反序列化值=CreateValueInternal(读取器,objectType,协定,null,null,null,null);} } } jsonserializeinternal reader类中的反序列化方法将尝试根据当前请求参数的类型找到并实例化合适的JsonConverter。如果找到匹配的转换器,转换器将用于实际的反序列化数据绑定操作。在当前的例子中,因为api的参数类型是Person,所以会匹配personjsonverter,这就是为什么我们通过添加personjsonverter来完成多态数据绑定的功能。
附加源代码
摘要
以上就是本文的全部内容。希望本文的内容对大家的学习或工作有一定的参考价值。有问题可以留言交流。谢谢你的支持。