一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - ASP.NET教程 - .NET Core WebApi中如何實現多態數據綁定實例代碼

.NET Core WebApi中如何實現多態數據綁定實例代碼

2020-06-01 14:32LamondLu ASP.NET教程

這篇文章主要給大家介紹了關于.NET Core WebApi中如何實現多態數據綁定的相關資料,文中通過示例代碼介紹的非常詳細,并給出來完整的實例代碼,需要的朋友可以參考借鑒,下面來一起學習學習吧

什么是.NET Core?

隨著2014年 Xamarin和微軟發起.NET基金會,微軟在2014年11月份 開放.NET框架源代碼。在.NET開源基金會的統一規劃下誕生了.NET Core 。也就是說.NET Core Framework是參考.NET Framework重新開發的.NET實現,Mono是.NET Framework的一個開源的、跨平臺的實現。

本文主要介紹了關于.NET Core WebApi多態數據綁定的相關內容,分享出來供大家參考學習,下面話不多說了,來一起看看詳細的介紹吧

什么是多態數據綁定?

我們都知道在ASP.NET Core WebApi中數據綁定機制(Data Binding)負責綁定請求參數, 通常情況下大部分的數據綁定都能在默認的數據綁定器(Binder)中正常的進行,但是也會出現少數不支持的情況,例如多態數據綁定。所謂的多態數據綁定(polymorphic data binding),即請求參數是子類對象的Json字符串, 而action中定義的是父類類型的變量,默認情況下ASP.NET Core WebApi是不支持多態數據綁定的,會造成數據丟失。

以下圖為例

.NET Core WebApi中如何實現多態數據綁定實例代碼

Person類是一個父類,Doctor類和Student類是Person類的派生類。Doctor類中持有的HospitalName屬性,Student中持有的SchoolName屬性。

接下來我們創建一個Web Api項目并添加一個PeopleController。

在PeopleController中我們添加一個Add api,并將請求數據直接返回,以便查看效果。

?
1
2
3
4
5
6
7
8
9
10
[Route("api/people")]
public class PeopleController : Controller
{
 [HttpPost]
 [Route("")]
 public List<Person> Add([FromBody]List<Person> people)
 {
 return people;
 }
}

這里我們使用Postman請求這個api, 請求的Content-Type是application/json, 請求的Body內容如下。

?
1
2
3
4
5
6
7
8
9
10
11
12
[{
 firstName: 'Mike',
 lastName: 'Li'
}, {
 firstName: 'Stephie',
 lastName: 'Wang',
 schoolName: 'No.15 Middle School'
}, {
 firstName: 'Jacky',
 lastName: 'Chen',
 hospitalName: 'Center Hospital'
}]

請求的返回內容

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
[
 {
 "FirstName": "Mike",
 "LastName": "Li"
 },
 {
 "FirstName": "Stephie",
 "LastName": "Wang"
 },
 {
 "FirstName": "Jacky",
 "LastName": "Chen"
 }
]

返回結果和我們希望得到的結果不太一樣,Student持有的SchoolName屬性和Doctor持有的HospitalName屬性都丟失了。

現在我們啟動項目調試模式,重新使用Postman請求一次,得到的結果如下

.NET Core WebApi中如何實現多態數據綁定實例代碼

People集合中存放3個People類型的對象, 沒有出現我們期望的Student類型對象和Doctor類型對象,這說明.NET Core WebApi默認是不支持多態數據綁定的,如果使用父類類型變量來接收數據,Data Binding只會實例化父類對象,而非一個派生類對象, 從而導致屬性丟失。

自定義JsonConverter來實現多態數據綁定

JsonConverter是Json.NET中的一個類,主要負責Json對象的序列化和反序列化。

首先我們創建一個泛型類JsonCreationConverter,并繼承了JsonConverter類,代碼如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public abstract class JsonCreationConverter<T> : JsonConverter
{
 public override bool CanWrite
 {
 get
 {
  return false;
 }
 }
 
 protected abstract T Create(Type objectType, JObject jObject);
 
 public override bool CanConvert(Type objectType)
 {
 return typeof(T).IsAssignableFrom(objectType);
 }
 
 
 public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
 {
 if (reader == null) throw new ArgumentNullException("reader");
 if (serializer == null) throw new ArgumentNullException("serializer");
 if (reader.TokenType == JsonToken.Null)
  return null;
 
 JObject jObject = JObject.Load(reader);
 T target = Create(objectType, jObject);
 serializer.Populate(jObject.CreateReader(), target);
 return target;
 }
 
 public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
 {
 throw new NotImplementedException();
 }
}

其中,我們加入了一個抽象方法Create,這個方法會負責根據Json字符串的內容,返回一個泛型類型對象,這里既可以返回一個當前泛型類型的對象,也可以返回一個當前泛型類型派生類的對象。JObject是Json.NET中的Json字符串讀取器,負責讀取Json字符串中屬性的值。

另外我們還復寫了ReadJson方法,在ReadJson中我們會先調用Create方法獲取一個當前泛型類對象或者當前泛型類的派生類對象(Json.NET中默認的KeyValuePairConverter會直接實例化當前參數類型對象,這也就是默認不支持多態數據綁定的主要原因),serializer.Popluate方法的作用是將Json字符串的內容映射到目標對象(當前泛型類對象或者當前泛型類的派生類對象)的對應屬性。

這里由于我們只需要讀取Json, 所以WriteJson的方法我們不需要實現,CanWrite屬性我們也強制返回了False。

第二步,我們創建一個PersonJsonConverter類,它繼承了JsonCreationConverter<Person>, 其代碼如下

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class PersonJsonConverter : JsonCreationConverter<Person>
{
 protected override Person Create(Type objectType, JObject jObject)
 {
 if (jObject == null) throw new ArgumentNullException("jObject");
 
 if (jObject["schoolName"] != null)
 {
  return new Student();
 }
 else if (jObject["hospitalName"] != null)
 {
  return new Doctor();
 }
 else
 {
  return new Person();
 }
 }
}

在這個類中我們復寫了Create方法,這里我們使用JObject來獲取Json字符串中擁有的屬性。

  • 如果字符串中包含schoolName屬性,就返回一個新的Student對象
  • 如果字符串中包含hospitalName屬性,就返回一個新的Doctor對象
  • 否則,返回一個新Person對象

最后一步,我們在Person類中使用特性標注Person類使用PersonJsonConverter來進行轉換Json序列化和反序列化。

?
1
2
3
4
5
6
7
[JsonConverter(typeof(PersonJsonConverter))]
public class Person
{
 public string FirstName { get; set; }
 
 public string LastName { get; set; }
}

現在我們重新使用調試模式啟動程序, 然后使用Postman請求當前api

 .NET Core WebApi中如何實現多態數據綁定實例代碼

我們會發現,people集合中已經正確綁定了的派生子類類型對象,最終Postman上我們得到以下響應結果

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[
 {
  "FirstName": "Mike",
  "LastName": "Li"
 },
 {
  "SchoolName": "No.15 Middle School",
  "FirstName": "Stephie",
  "LastName": "Wang"
 },
 {
  "HospitalName": "Center Hospital",
  "FirstName": "Jacky",
  "LastName": "Chen"
 }
]

至此多態數據綁定成功。

刨根問底

為什么添加了一個PersonJsonConverter類,多態綁定就實現了呢?

讓我們來一起Review一下MVC Core以及Json.NET的代碼。

首先我們看一下MvcCoreMvcOptionsSetup代碼

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class MvcCoreMvcOptionsSetup : IConfigureOptions<MvcOptions>
{
 private readonly IHttpRequestStreamReaderFactory _readerFactory;
 private readonly ILoggerFactory _loggerFactory;
 
 ......
  
 public void Configure(MvcOptions options)
 {
  options.ModelBinderProviders.Add(new BinderTypeModelBinderProvider());
  options.ModelBinderProviders.Add(new ServicesModelBinderProvider());
  options.ModelBinderProviders.Add(new BodyModelBinderProvider(options.InputFormatters, _readerFactory, _loggerFactory, options));
  ......
 }
 
 ......
 
}

MvcCoreMvcOptionsSetup類中的Configure方法設置了默認數據綁定使用Provider列表。

當一個api參數被標記為[FromBody]時,BodyModelBinderProvider會實例化一個BodyModelBinder對象來處理這個參數并嘗試進行數據綁定。

BodyModelBinder類中有一個BindModelAsync方法,從名字的字面意思上我們很清楚的知道這個方法就是用來綁定數據的。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public async Task BindModelAsync(ModelBindingContext bindingContext)
{
 if (bindingContext == null)
 {
  throw new ArgumentNullException(nameof(bindingContext));
 }
 
  ….
 
 var formatter = (IInputFormatter)null;
 for (var i = 0; i < _formatters.Count; i++)
 {
   if (_formatters[i].CanRead(formatterContext))
  {
   formatter = _formatters[i];
   _logger?.InputFormatterSelected(formatter, formatterContext);
   break;
  }
  else
  {
    logger?.InputFormatterRejected(_formatters[i], formatterContext);
  }
 }
 
 ……
 
 try
 {
  var result = await formatter.ReadAsync(formatterContext);
 
  ……
 }
 catch (Exception exception) when (exception is InputFormatterException || ShouldHandleException(formatter))
 {
  bindingContext.ModelState.AddModelError(modelBindingKey, exception, bindingContext.ModelMetadata);
 }
}

在這個方法中它會嘗試尋找一個匹配的IInputFormatter對象來綁定數據,由于這時候請求的Content-Type是application/json, 所以這里會使用JsonInputFormatter對象來進行數據綁定。

下面我們看一下JsonInputFormatter類的部分關鍵代碼

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public override async Task<InputFormatterResult> ReadRequestBodyAsync(
   InputFormatterContext context,
   Encoding encoding)
{
 ......
 
 using (var streamReader = context.ReaderFactory(request.Body, encoding))
 {
  using (var jsonReader = new JsonTextReader(streamReader))
  {
   
 
   object model;
   try
   {
    model = jsonSerializer.Deserialize(jsonReader, type);
   }
   finally
   {
    jsonSerializer.Error -= ErrorHandler;
    ReleaseJsonSerializer(jsonSerializer);
   }
 
   
  }
 }
}

JsonInputFormatter類中的ReadRequestBodyAsync方法負責數據綁定, 在該方法中使用了Json.NET的JsonSerializer類的Deserialize方法來進行反序列化, 這說明Mvc Core的底層是直接使用Json.NET來操作Json的。

JsonSerializer類的部分關鍵代碼

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public object Deserialize(JsonReader reader, Type objectType)
{
 return DeserializeInternal(reader, objectType);
}
 
internal virtual object DeserializeInternal(JsonReader reader, Type objectType)
{
 ……
 
 JsonSerializerInternalReader serializerReader = new JsonSerializerInternalReader(this);
 object value = serializerReader.Deserialize(traceJsonReader ?? reader, objectType, CheckAdditionalContent);
 
 ……
 return value;
}

JsonSerializer會調用JsonSerializerInternalReader類的Deserialize方法將Json字符串內容反序列化。

最終我們看一下JsonSerializerInternalReader中的部分關鍵代碼

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public object Deserialize(JsonReader reader, Type objectType, bool checkAdditionalContent)
{
 
 
 JsonConverter converter = GetConverter(contract, null, null, null);
 
 if (reader.TokenType == JsonToken.None && !reader.ReadForType(contract, converter != null))
 {
  ......
 
  object deserializedValue;
 
  if (converter != null && converter.CanRead)
  {
   deserializedValue = DeserializeConvertable(converter, reader, objectType, null);
  }
  else
  {
   deserializedValue = CreateValueInternal(reader, objectType, contract, null, null, null, null);
  }
  }
}

JsonSerializerInternalReader類里面的Deserialize方法會嘗試根據當前請求參數的類型,去查找并實例化一個合適的JsonConverter。 如果查找到匹配的Converter, 就使用該Converter進行實際的反序列化數據綁定操作。在當前例子中由于api的參數類型是Person,所以它會匹配到PersonJsonConverter, 這就是為什么我們通過添加PersonJsonConverter就完成了多態數據綁定的功能。

附源代碼

總結

以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對服務器之家的支持。

原文鏈接:https://www.cnblogs.com/lwqlun/p/9532803.html

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 好大好硬好长好爽a网站 | 私人家庭影院5577 | 色啪久久婷婷综合激情 | 日本www午夜色在线视频 | 亚洲欧洲淘宝天堂日本 | 日本最新伦中文字幕 | 非洲特级特黄aa大片 | 美女尿口照片 | 亚洲精品国精品久久99热 | 亚洲图片一区二区 | 色中色导航| 色人阁图片 | 欧美久久一区二区三区 | 欧美一级片在线视频 | 亚洲天堂影院在线观看 | 日本大片免a费观看在线 | 嫩草在线视频www免费观看 | 欧美一卡2卡三卡4卡5卡免费观看 | 亚洲 综合 欧美在线 热 | 午夜理伦片免费 | a级情欲片在线观看hd | 国产a高清| 免费观看在线aa | 精品夜夜澡人妻无码AV蜜桃 | 99在线精品免费视频九九视 | 欧美日韩一区二区综合在线视频 | 亚洲性久久久影院 | 欧美精选视频 | 高清国产精品久久久久 | 欧美同志video 在线观看 | 日产中文乱码卡一卡二 | 慢慢娇淫 | 69罗莉视频在线观看 | 嫩草影院永久在线一二三四 | 美女gif跪趴式抽搐动态图 | 亚洲春色综合另类网蜜桃 | 四虎在线视频免费观看视频 | 四虎成人影院 | 精品亚洲欧美中文字幕在线看 | 18欧美同性videos可播放 | 亚洲精品有码在线观看 |