天天看點

ASP.NET Web API Model-ParameterBindingASP.NET Web API Model-ParameterBinding

通過上個篇幅的學習了解Model綁定的基礎知識,然而在ASP.NET Web API中Model綁定功能子產品并不是被直接調用的,而是要通過本篇要介紹的内容ParameterBinding的一系列對象對其進行封裝調用,通過本篇的學習之後也會大概的清楚在Web API中綁定會有哪幾種方式。

在ASP.NET Web API中ParameterBinding代表着參數綁定并且在這其中涉及了幾種綁定的方式,然而ParaMeterBinding并不是單獨執行的,就好比一個控制器方法中有可能會有多個參數一樣,是以我們就先來看一下ActionBinding的相關對象,對于這些對象的生成的環境以及過程我們在後面的篇幅中會有講解。

HttpActionBinding

代碼1-1

1

2

3

4

5

6

7

8

9

10

11

12

<code>namespace</code> <code>System.Web.Http.Controllers</code>

<code>{</code>

<code>    </code><code>public</code> <code>class</code> <code>HttpActionBinding</code>

<code>    </code><code>{</code>

<code>        </code><code>public</code> <code>HttpActionBinding();</code>

<code>        </code><code>public</code> <code>HttpActionBinding(HttpActionDescriptor actionDescriptor, HttpParameterBinding[] bindings);</code>

<code>        </code><code>public</code> <code>HttpActionDescriptor ActionDescriptor { </code><code>get</code><code>; </code><code>set</code><code>; }</code>

<code>        </code><code>public</code> <code>HttpParameterBinding[] ParameterBindings { </code><code>get</code><code>; </code><code>set</code><code>; }</code>

<code>        </code><code>public</code> <code>virtual</code> <code>Task ExecuteBindingAsync(HttpActionContext actionContext, CancellationToken cancellationToken);</code>

<code>    </code><code>}</code>

<code>}</code>

代碼1-1中對于HttpActionBinding類型的定義一目了然,看HttpActionBinding類型的重載構造函數中有HttpActionDescriptor類型、HttpParameterBinding類型的數組類型作為參數,HttpActionDescriptor類型作為控制器方法描述類型大家已經很熟悉了吧,這個類型中包含着控制器方法的中繼資料資訊,而後者HttpParameterBinding類型則是表示着參數綁定對象,其實在Model綁定中每個參數的綁定都是通過ParameterBinding來綁定的,是以這裡的執行過程我們暫時不去深入了解,就是單純的了解一下相關的對象模型。

HttpParameterBinding

代碼1-2

13

14

15

16

<code>    </code><code>public</code> <code>abstract</code> <code>class</code> <code>HttpParameterBinding</code>

<code>        </code><code>protected</code> <code>HttpParameterBinding(HttpParameterDescriptor descriptor);</code>

<code>        </code><code>public</code> <code>HttpParameterDescriptor Descriptor { </code><code>get</code><code>; }</code>

<code>        </code><code>public</code> <code>virtual</code> <code>string</code> <code>ErrorMessage { </code><code>get</code><code>; }</code>

<code>        </code><code>public</code> <code>bool</code> <code>IsValid { </code><code>get</code><code>; }</code>

<code>        </code><code>public</code> <code>virtual</code> <code>bool</code> <code>WillReadBody { </code><code>get</code><code>; }</code>

<code>        </code><code>public</code> <code>abstract</code> <code>Task ExecuteBindingAsync(ModelMetadataProvider metadataProvider, HttpActionContext actionContext, CancellationToken cancellationToken);</code>

<code>        </code><code>protected</code> <code>object</code> <code>GetValue(HttpActionContext actionContext);</code>

<code>        </code><code>protected</code> <code>void</code> <code>SetValue(HttpActionContext actionContext, </code><code>object</code> <code>value);</code>

在代碼1-2中我們看到HttpParameterBinding類型的定義,可以看到HttpParameterBinding是抽象類型,并且實作綁定的方法ExecuteBindingAsync()也是抽象的,這也是為了在不同的環境和情況相愛采取不同的綁定機制做好鋪墊就是多态啦,這個我們後面會說,我們還是先看下HttpParameterBinding類型的内部定義,首先我們看到的是構造函數中的參數類型,HttpParameterDescriptor類型,又是一個對象描述類型,這次的描述對象則是控制其方法中的某個參數,而HttpParameterBinding對象執行個體的生成則是根據HttpParameterDescriptor對象執行個體而來,這個後續的篇幅中會有講解。ErrorMessage屬性表示在ParameterBinding綁定的過程中出現的錯誤資訊,而IsValid屬性表示綁定是否可以完成的依據就是判斷ErrorMessage屬性是否為Null,WillReadBody屬性表示ParameterBinding對象是否讀取Http消息正文内容作為參數綁定的資料源,這個稍後給大家看的示例中就有,一看便知。GetValue()和SetValue()方法就是向目前HttpActionContext中擷取對應的參數值和設定參數對應值。

ModelBinderParameterBinding

代碼1-3

<code>namespace</code> <code>System.Web.Http.ModelBinding</code>

<code>    </code><code>public</code> <code>class</code> <code>ModelBinderParameterBinding : HttpParameterBinding, IValueProviderParameterBinding</code>

<code>        </code><code>public</code> <code>ModelBinderParameterBinding(HttpParameterDescriptor descriptor, IModelBinder modelBinder, IEnumerable&lt;System.Web.Http.ValueProviders.ValueProviderFactory&gt; valueProviderFactories);</code>

<code>        </code><code>public</code> <code>IModelBinder Binder { </code><code>get</code><code>; }</code>

<code>        </code><code>public</code> <code>IEnumerable&lt;System.Web.Http.ValueProviders.ValueProviderFactory&gt; ValueProviderFactories { </code><code>get</code><code>; }</code>

<code>        </code><code>public</code> <code>override</code> <code>Task ExecuteBindingAsync(ModelMetadataProvider metadataProvider, HttpActionContext actionContext, CancellationToken cancellationToken);</code>

代碼1-3表示的是ModelBinderParameterBinding類型,這個類型代表的綁定方式(綁定的資料來源)是通過IModelBinder來擷取的,也就正如代碼1-3中我們看到的構造函數一樣,其中有IModelBinder和ValueProviderFactory類型的集合,這些就是資料綁定的資料源。Binder屬性和ValueProviderFactories屬性也就是構造函數傳入的兩個類型值。對于綁定的細節這裡就不說多了。

FormatterParameterBinding

代碼1-4

<code>    </code><code>public</code> <code>class</code> <code>FormatterParameterBinding : HttpParameterBinding</code>

<code>        </code><code>public</code> <code>FormatterParameterBinding(HttpParameterDescriptor descriptor, IEnumerable&lt;MediaTypeFormatter&gt; formatters, IBodyModelValidator bodyModelValidator);</code>

<code>        </code><code>public</code> <code>IBodyModelValidator BodyModelValidator { </code><code>get</code><code>; </code><code>set</code><code>; }</code>

<code>        </code><code>public</code> <code>override</code> <code>string</code> <code>ErrorMessage { </code><code>get</code><code>; }</code>

<code>        </code><code>public</code> <code>IEnumerable&lt;MediaTypeFormatter&gt; Formatters { </code><code>get</code><code>; </code><code>set</code><code>; }</code>

<code>        </code><code>public</code> <code>override</code> <code>bool</code> <code>WillReadBody { </code><code>get</code><code>; }</code>

<code>        </code><code>public</code> <code>virtual</code> <code>Task&lt;</code><code>object</code><code>&gt; ReadContentAsync(HttpRequestMessage request, Type type, IEnumerable&lt;MediaTypeFormatter&gt; formatters, IFormatterLogger formatterLogger);</code>

代碼1-4中所示的FormatterParameterBinding類型是通過哪種方式來擷取綁定資料源的呢?大家可以看到WillReadBody屬性在這個類型中被重寫了,原來在HttpParameterBinding類型中預設為false的是不會對Http請求的正文内容進行讀寫的,而這裡在FormatterParameterBinding類型中,已然的重寫了,說明在這個類型中我們所能用到的綁定資料源就是從Http請求的正文内容來讀取了,然而Http請求的正文内容并不是直接放在那裡供我們讀取的,而是在用戶端發送前就已經被指定的序列化器序列化了。

那麼我們在伺服器端就要反序列化才能擷取到值,這裡在代碼1-4中我們看到構造函數中可以看到和代碼1-3一樣都是具有一個HttpParameterDescriptor類型的參數對象,而後則是一個MediaTypeFormatter類型的集合對象,最後是進行Model驗證的對象,這個後續再說。

現在我們就來看看第二個參數類型中的MediaTypeFormatter類型。

MediaTypeFormatter

代碼1-5

<code>namespace</code> <code>System.Net.Http.Formatting</code>

<code>    </code><code>public</code> <code>abstract</code> <code>class</code> <code>MediaTypeFormatter</code>

<code>        </code><code>protected</code> <code>MediaTypeFormatter();</code>

<code>        </code><code>public</code> <code>abstract</code> <code>bool</code> <code>CanReadType(Type type);</code>

<code>        </code><code>public</code> <code>abstract</code> <code>bool</code> <code>CanWriteType(Type type);</code>

<code>        </code><code>public</code> <code>virtual</code> <code>Task&lt;</code><code>object</code><code>&gt; ReadFromStreamAsync(Type type, Stream readStream, HttpContent content, IFormatterLogger formatterLogger);</code>

<code>        </code><code>public</code> <code>virtual</code> <code>Task WriteToStreamAsync(Type type, </code><code>object</code> <code>value, Stream writeStream, HttpContent content, TransportContext transportContext);</code>

在代碼1-5中所示的MediaTypeFormatter類型是被删減過的一部分,所剩4個方法中兩個是抽象方法兩個是虛方法,先看兩個抽象方法的定義,CanReadType()方法表示的是根據指定的參數類型從目前的Http請求正文中能否讀取到改類型的值,簡單來說就是能否把正文内容反序列化為指定的參數類型,同理CanWriterType()是對響應正文進行操作判斷。而ReadFromStreamAsync()方法和WriteToStreamAsync()方法則是對Http請求正文内容和Http響應正文内容進行實際操作的兩個方法。

對于MediaTypeFormatter類型的實作類型比如說針對Json格式的JsonMediaTypeFormatter和針對Xml格式的XmlMediaTypeFormatter.

我們看一下JsonMediaTypeFormatter類型的簡單示例,大家就會知道是怎麼回事了。

首先我們在伺服器端的控制器中定義一個接收Post請求的方法,

代碼1-6

<code>        </code><code>[CustomControllerActionAuthorizationFilter]</code>

<code>        </code><code>public</code> <code>void</code> <code>Post(Product product)</code>

<code>        </code><code>{</code>

<code>            </code><code>products.Add(product);</code>

<code>        </code><code>}</code>

為了友善示範在其控制其方法上加了個授權過濾器,然後我們對Post請求的處理使用JsonMediaTypeFormatter類型進行反序列化的操作将在CustomControllerActionAuthorizationFilter過濾器類型中進行。

代碼1-7

<code>public</code> <code>Task&lt;System.Net.Http.HttpResponseMessage&gt; ExecuteAuthorizationFilterAsync(System.Web.Http.Controllers.HttpActionContext actionContext, System.Threading.CancellationToken cancellationToken, Func&lt;Task&lt;System.Net.Http.HttpResponseMessage&gt;&gt; continuation)</code>

<code>            </code><code>Console.WriteLine(actionContext.Request.Content.Headers.ContentType.MediaType);</code>

<code>            </code><code>IEnumerable&lt;MediaTypeFormatter&gt; formatters = </code><code>new</code> <code>MediaTypeFormatter[] </code>

<code>            </code><code>{</code>

<code>                </code><code>new</code> <code>JsonMediaTypeFormatter()</code>

<code>            </code><code>};</code>

<code>            </code><code>Product product = actionContext.Request.Content.ReadAsAsync&lt;Common.Product&gt;(formatters).Result;</code>

<code>            </code><code>Console.WriteLine(product.ProductID);</code>

<code>            </code><code>Console.WriteLine(product.ProductName);</code>

<code>            </code><code>Console.WriteLine(product.ProductCategory);</code>

<code>            </code><code>return</code> <code>continuation();</code>

在代碼1-7中,在請求到達控制器方法之前會先經過授權過濾器,在這其中首先我們是先向伺服器端的控制器輸出了目前請求的格式類型,然後就對目前請求的正文内容使用JsonMediaTypeFormatter類型進行反序列化操作。

我們再看下用戶端使用HttpClient類型進行模拟Post請求,

代碼1-8

<code>            </code><code>HttpClient httpClient = </code><code>new</code> <code>HttpClient();</code>

<code>            </code><code>Product product = </code><code>new</code> <code>Product()</code>

<code>                </code><code>ProductID = </code><code>"003"</code><code>,</code>

<code>                </code><code>ProductName = </code><code>"旺仔牛奶"</code><code>,</code>

<code>                </code><code>ProductCategory = </code><code>"食品類"</code>

<code>            </code><code>await httpClient.PostAsJsonAsync&lt;Product&gt;(</code><code>"http://localhost/selfhost/api/product"</code><code>, product);</code>

最後結果如圖1.

圖1

<a href="http://s3.51cto.com/wyfs02/M02/49/F6/wKiom1QgH5Wi-8LAAACfAIHT_nY932.jpg" target="_blank"></a>

看到這裡想必大家也知道對于XmlMediaTypeFormatter的使用方式是怎樣的了。

還有一種MediaTypeFormatter類型FormUrlEncodedMediaTypeFormatter類型,FormUrlEncodedMediaTypeFormatter類型表示的是在Http Post請求中表單送出的資料所用格式器,我們看一下FormUrlEncodedMediaTypeFormatter類型中CanReadType()方法的實作。

代碼1-9

<code>    </code><code>public</code> <code>override</code> <code>bool</code> <code>CanReadType(Type type)</code>

<code>        </code><code>if</code> <code>(type == </code><code>null</code><code>)</code>

<code>            </code><code>throw</code> <code>Error.ArgumentNull(</code><code>"type"</code><code>);</code>

<code>        </code><code>if</code> <code>(!(type == </code><code>typeof</code><code>(FormDataCollection)))</code>

<code>            </code><code>return</code> <code>FormattingUtilities.IsJTokenType(type);</code>

<code>        </code><code>return</code> <code>true</code><code>;</code>

在代碼1-9我們可以看到FormDataCollection類型實際就是IEnumerable&lt;KeyValuePair&lt;string,string&gt;&gt;類型的對象,在表單送出後的請求Url中大家也都可以看到是是key=value的形式,是以這裡就是這種格式的。對于這個格式器的示例會在後面的篇幅給大家做示範講解。

HttpRequestParameterBinding

最後我們再看一個RequestParameterBinding對象。

代碼1-10

<code>    </code><code>public</code> <code>class</code> <code>HttpRequestParameterBinding : HttpParameterBinding</code>

<code>        </code><code>// Methods</code>

<code>        </code><code>public</code> <code>HttpRequestParameterBinding(HttpParameterDescriptor descriptor)</code>

<code>            </code><code>: </code><code>base</code><code>(descriptor)</code>

<code>        </code><code>public</code> <code>override</code> <code>Task ExecuteBindingAsync(ModelMetadataProvider metadataProvider, HttpActionContext actionContext, CancellationToken cancellationToken)</code>

<code>            </code><code>string</code> <code>parameterName = </code><code>base</code><code>.Descriptor.ParameterName;</code>

<code>            </code><code>HttpRequestMessage request = actionContext.ControllerContext.Request;</code>

<code>            </code><code>actionContext.ActionArguments.Add(parameterName, request);</code>

<code>            </code><code>return</code> <code>TaskHelpers.Completed();</code>

在代碼1-10中我們看到再ExecuteBindingAsync()方法的實作中,是直接擷取到參數名稱和目前的請求對象,然後添加到控制其方法執行上下文的ActionArguments屬性中,在之前的篇幅中也說過這個屬性用來存放方法對應參數值(Model綁定後的值)。

本篇關于ParameterBinding的對象介紹就到這裡,都是零零散散的不過在後面的篇幅中都會有示例介紹說明的。

     本文轉自jinyuan0829 51CTO部落格,原文連結:http://blog.51cto.com/jinyuan/1557178,如需轉載請自行聯系原作者