文章出處

本文將概述在WebAPI方式下將如何將參數綁定到一個action方法,包括參數是如何被讀取,一系列規則決定特定環境采用的那種綁定方式,文章最后將給出一些實際的例子。

Parameter binding說到底是接到一個Http請求,將其轉換成.NET類型使得action方法的簽名更易于理解。

請求消息(request message)包括了請求的所有信息,如帶查詢字符串的請求地址(URL),內容主體(content body)及頭部信息(header)。在沒有采用parameter binding

的情況下,每個action方法將需要接收request message,并手動從中提取出參數,如下所示:

public object MyAction(HttpRequestMessage request)
{
        // make explicit calls to get parameters from the request object
        int id = int.Parse(request.RequestUri.ParseQueryString().Get("id")); // need error logic!
        Customer c = request.Content.ReadAsAsync<Customer>().Result; // should be async!
        // Now use id and customer
}

很顯然,這樣的方式丑陋,易出錯,代碼重復,而且難以單元測試。我們希望action的簽名類似以下的形式:

public object MyAction(int id, Customer c) { }

那么WebAPI是如何將request message轉換成像id和customer這樣的參數的呢?

Model Binding vs. Formatters

參數綁定有兩種技術:Model Binding和Formatters。實際上,WebAPI使用model binding讀取查詢字符串(query string)內容進行參數綁定,使用Formatters讀取主體內容

(body content)進行參數的綁定。

  • Using Model Binding:

ModelBinding和MVC中的此概念是一致的,更多內容見Here。通常有一個"ValuePeoviders"提供數據片斷如查詢字符串參數,model binder將這些片斷組合成一個對象。

  • Using Formatters:

Formatters(如MediaTypeFormatter類所示)實際上是包含額外元數據的序列化程序。WebAPI從HttpConfiguration中獲取一個formatters的列表,然后通過request信息

中的content-type來判斷采用具體合適的formatter。WebAPI有不少默認的formatters。默認的JSON formatter是JSON.NET。還有Xml formatter和采用JQuery語法的

FormUrl formatter。

其中Formatters的核心方法是MediaTypeFormatter.ReadFromStreamAsync,如下所示:

public virtual Task<object> ReadFromStreamAsync(
Type type,
Stream stream,
HttpContentHeaders contentHeaders,
IFormatterLogger formatterLogger)

其中,Type指代參數的類型,將傳遞給序列化器serializer。Stream是請求信息的content stream。Read方法將讀取stream,將其實例化為一個對象,然后返回它。

HttpContentType來自請求信息。IFormatterLogger是一個回調接口,fomatter正是通過此接口來記錄讀取中的錯誤。

model binding和formatter都支持驗證和錯誤信息記錄。然后,相比formatter而言model binding更靈活。

何時采用特定的參數綁定方式?(when do we use which?)

以下這些基本原則決定了parameter是通過modelbinding還是formatter來讀取的:

如果參數未添加任何特性字段[attribute]標明,那么這將由參數的.NET類型來決定具體采用何種方式。簡單類型"Simple types"采用model binding。復雜類型"Complex types"

  1. 采用formatters。簡單類型包括:primitives,Timespan,DateTime,Guid,Decimal,String,或者通過類型轉換器(TypeConverter)從字符串轉換過來的類型。
  2. 通過使用[FormBody]特性標示特定參數應該從body中取值。
  3. 通過使用[ModelBinder]特性來標示參數或參數類型應該使用model bound方式。這個特性也可以用來配置model binder。[FromUri]是一個從[ModelBinder]繼承而來的
    實體,用于配置model binder只應用到URL中的數據。
  4. body只能被讀取一次。如果簽名中有兩個負責類型,至少其中一個必須添加[ModelBinder]特性標注。

以下是使得這些原則得以穩定并可預測的關鍵設計。

(body 只能被讀取一次)Only one thing can read the body

MVC和WebAPI之間的一個關鍵不同點在于MVC緩存請求主體(request body)。這意味著MVC的參數綁定可以反復從body中查找參數片斷。然而,在WebAPI中,請求主體(HttpContent)

只能被讀取一次,不被緩存,只能向前讀取的流。這意味著parameter binding需要謹慎對待stream,除非在需要綁定參數的情況下,否則stream不能被讀取。

以下的action方法想直接讀取stream,因而導致WebAPI不能保證其擁有用于參數綁定的stream。考慮如下的action方法:

        // Action saves the request’s content into an Azure blob 
        public Task PostUploadfile(string destinationBlobName)
        {
            // string should come from URL, we’ll read content body ourselves.
            Stream azureStream = OpenAzureStorage(destinationBlobName); // stream to write to azure
            return this.Request.Content.CopyToStream(azureStream); // upload body contents to azure. 
        }

參數是一個簡單類型,因而這將從查詢字符串中讀取。由于action簽名中并不包含任何 負責類型,WebAPI將永遠不會讀取request content stream,因而這里的action方法可以讀取它。

示例

以下給出一些不同請求的示例說明它們將如何映射到特定action簽名:

/?id=123&name=bob 
void Action(int id, string name) // 所有參數都是簡單類型,因而都將來自url

 

/?id=123&name=bob 
void Action([FromUri] int id, [FromUri] string name) // 同上

void Action([FromBody] string name); //[FormBody]特性顯示標明讀取整個body為一個字符串作為參數

public class Customer {   // 定義的一個復雜對象類型 
  public string Name { get; set; } 
  public int Age { get; set; } 
}

/?id=123 
void Action(int id, Customer c) // 參數id從query string中讀取,參數c是一個復雜Customer對象類戲,通過formatter從body中讀取

void Action(Customer c1, Customer c2) // 出錯!多個參數都是復雜類型,都試圖從body中讀取,而body只能被讀取一次

void Action([FromUri] Customer c1, Customer c2) // 可以!不同于上面的action,復雜類型c1將從url中讀取,c2將從body中讀取

void Action([ModelBinder(MyCustomBinder)] SomeType c) // 標示使用特定的model binder來解析參數

[ModelBinder(MyCustomBinder)] public class SomeType { } // 通過給特定類型SomeType聲明標注[ModelBidner(MyCustomBinder)]特性使得所有SomeType類型參數應用此規則 

void Action(SomeType c) // 由于c的類型為SomeType,因而應用SomeType上的特性決定其采用model binding

與MVC的區別

以下是MVC和WebAPI在參數綁定上的一些不同點:

  1. MVC只具有model binding,而沒有formatters。這是由于MVC將對request body也應用model bind 進行解析,而WebAPI對request body將使用serializer來解析。
  2. MVC將緩存request body,因而能夠很容易的將其傳遞給model binding。WebAPI不緩存request body,因而默認將不對request body應用model binding解析。
  3. WebAPI的綁定完全可以通過action簽名的類型來決定。比如:在WebAPI中,我們知道一個參數最終將從body還是query string中讀取綁定。然而,在MVC中,model binding
    系統將同時查找body和query string數據進行解析綁定。

原文:How WebAPI does Parameter Binding


文章列表


不含病毒。www.avast.com
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()