細說 Web API參數綁定和模型綁定

来源:http://www.cnblogs.com/chenlinzhi/archive/2016/02/28/5224204.html
-Advertisement-
Play Games

今天跟大家分享下在Asp.NET Web API中Controller是如何解析從客戶端傳遞過來的數據,然後賦值給Controller的參數的,也就是參數綁定和模型綁定。 Web API參數綁定就是簡單類型的綁定,比如:string,char,bool,int,uint,byte,sbyte,sho


 今天跟大家分享下在Asp.NET Web API中Controller是如何解析從客戶端傳遞過來的數據,然後賦值給Controller的參數的,也就是參數綁定和模型綁定。

Web API參數綁定就是簡單類型的綁定,比如:string,char,bool,int,uint,byte,sbyte,short,ushort,long, float這些基元類型。模型綁定就是除此之外的複雜類型的綁定。大家都知道在MVC中模型綁定都是通過預設的DefaultModelBinder來綁定的,沒有Get請求和POST請之分。然而在Web API中參數和模型綁定的機制在Get請求和POST請求是不一樣的。

一:參數綁定(簡單類型綁定)    

Web API參數綁定時,Action預設是從路由數據(url片段)和querystring中獲取數據的。我們都知道,Get請求一個服務的時候,客戶端是把數據放在URL中發送到伺服器端的;而POST請求是把數據放到請求體(Request Body)發送到伺服器端的。所以預設情況下在WebAPI中我們只能用GET請求去發送簡單類型的數據到伺服器端,然後Action再獲取數據,舉個慄子:

準備模型:

  public class Number
    {
        public int A { get; set; }
        public int B { get; set; }
        public Operation Operation { get; set; }
    }
    public class Operation
    {
        public string Add{get;set;}
        public string Sub { get; set; }
    }
View Code

配置路由:

      config.Routes.MapHttpRoute(
                name: "ActionApi",
                routeTemplate: "api/norestful/{controller}/{action}/{id}",
                defaults: new {id = RouteParameter.Optional}
                );
View Code

WebAPI Controller如下:

   public class ValuesController : ApiController
    {
        [HttpGet, HttpPost]
        public int SubNumber(int a,int b)
        {
            return a - b;
        }
}
View Code

客戶端ajax調用如下:

    function  ajaxOp(url,type,data,contentType) {
        $.ajax({
            url: url,
            type: type,
            data: data,
            contentType:contentType
            success:function(result) {
                alert(result);
            }
        });
    }
    function a() {
        var data = { a: 1, b: 2 };
        ajaxOp('/api/norestful/Values/SubNumber', 'POST',data);
    }
View Code

輸出結果如下:

 

如果換成POST請求,則找不到匹配的action

出現這種情況的原因主要是因為簡單類型的綁定預設情況下是利用【FromUri】特性來解析數據的,光看名稱就知道它只負責從URL中讀取數據,在後面複雜類型綁定時會講到【FromUri】的用法。

當然你要在POST請求下去綁定簡單類型,也是可以的,有三種辦法可以解決。

方法一:請求的數據以querystring的方式把數據放在URL中,POST空數據。

  function a() {
        var data = { a: 1, b: 2 };
        ajaxOp('/api/norestful/Values/SubNumber?'+$.param(data), 'POST');
}

方法二:手動從請求中讀取數據:

  POST請求下:

[HttpPost]
        public async Task<IHttpActionResult> AddNumbers()  
        {
            if (Request.Content.IsFormData())
            {
                NameValueCollection values = await Request.Content.ReadAsFormDataAsync();
                int a, b;
                int.TryParse(values["a"], out a);
                int.TryParse(values["b"], out b);
                return Ok(a - b);
            }
            return StatusCode(HttpStatusCode.BadRequest);
        }
View Code

此方法能解決問題,但是和模型綁定無關,ReadAsFormDataAsync是HttpRequestMessage類HttpContent屬性的一個擴展方法,該方法負責解析請求頭中content-type類型為application/x-www-form-urlencoded類型中的數據。

  Get請求下:

  [HttpGet]
        public IHttpActionResult SubNumberByGet() 
        {
            Dictionary<string, string> dic = Request.GetQueryNameValuePairs().ToDictionary(x => x.Key, x => x.Value);
            int a, b;
            int.TryParse(dic["a"], out a);
            int.TryParse(dic["b"], out b);
            return Ok(a-b);
        }
View Code

方法三:利用【FromBody】特性,此特性將從請求體(Request body)中獲取數據。但是【FromBody】特性只能用於Action中的一個參數,如果這樣寫:

  [HttpGet, HttpPost]
        public int SubNumber([FromBody]int a,[FromBody]int b)
        {
            return a - b;
        }

將會拋出無法將多個參數綁定到請求的異常:

所以在POST請求下綁定一個簡單類型利用【FromBody】特性還是可以的,也是最常用的解決方案。

 

之所以應用【FromBody】特性綁定多個簡單類型拋出異常的原因就是在Web API框架下,請求體(request body)中的數據會以stream流的方式發送到伺服器端,而我們沒辦法在模型綁定系統讀取stream流中數據後再去改變它;而不像在MVC框架下在請求開始之前請求體(RequestBody)中的數據被處理後以鍵值對的方式存儲在記憶體當中,所以MVC Controller中綁定多個複雜類型是沒有問題的。同樣在Web API框架下預設一個Action也不能同時綁定多個複雜類型,這點後面會講到,同時也會提供同時綁定多個複雜類型的相關解決方案。

 

在Web API框架下,參數綁定(簡單類型綁定)讀取數據有三種不同的方式:

1:Web API首先檢測參數是否應用了【FromBody】特性,如果有,就從該特性直接從請求體中讀取數據。

2:如果沒有【FromBody】特性,Web API就從綁定規則中讀取數據,綁定規則通過在WebApiConfig文件中設置HttpConfiguration的ParameterBindingRules屬性來實現,比如:config.ParameterBindingRules.Insert(0, typeof(Number), o => o.BindWithAttribute(new FromUriAttribute())),指的就是在項目中Number類型預設都是通過【FromUri】特性來獲取數據,這樣不必顯示提供【FromUri】特性了。

3:如果沒有【FromBody】特性,沒有綁定規則,則通過【FroUri】特性讀取數據,這也是參數綁定的預設行為。

 

二:模型綁定(複雜類型綁定)

Web API複雜類型綁定時候,Action預設從請求體(request body)中獲取數據,所以預設只能用POST請求去發送複雜類型到伺服器端,舉個慄子:

Action如下:

  [HttpGet,HttpPost]
        public int AddNumber(Number number)
        {
            return number.A + number.B;
        }

客戶端ajax如下:

   function b() {
        var data = { a: 1, b: 2};
        ajaxOp('/api/norestful/Values/AddNumber', 'POST', data);
}

Action在預設POST請求下,只能綁定一個複雜類型,如果綁定多個複雜類型,將會拋出異常,原因前面已經提到過。如果要綁定多個複雜類型,至少有四個辦法可以解決。

方法一:在GET請求下,所有類型應用【FromUri】特性。

Action如下:

   [HttpGet, HttpPost]
        public int OpNumbers([FromUri]Number number,[FromUri] Operation op)
        {
            return op.Add ? number.A + number.B : number.A - number.B;
        }

客戶端ajax如下:

   function d() {
        var data = { a: 1, b: 2, add: true, sub: false }
        ajaxOp('/api/norestful/Values/OpNumbers', 'GET', data);
}

方法二:手動從請求中讀取數據,具體實現方法跟上面簡單類型手動從請求中讀取數據的方法是一樣的,就不多講了。

方法三:在GET請求下利用嵌套複雜類型綁定數據,並應用【FromUri】特性
Action如下:

[HttpGet]
        public int OpNumbersByNestedClass([FromUri]Number number)
        {
            return number.Operation.Add ? number.A + number.B : number.A - number.B;
        }

客戶端ajax如下:

function b2() {
        var data = { a: 1, b: 2, add: true, sub: false };
        ajaxOp('/api/norestful/Values/OpNumbersByNestedClass', 'GET', {
            'number.a': data.a,
            'number.b': data.b,
            'number.operation.add': data.add,
            'number.operation.sub': data.sub
        });
}

方法四:POST請求下:

Action如下:

  [HttpGet,HttpPost]
        public int OpNumbersByNestedClass(Number number)
        {
            return number.Operation.Add ? number.A + number.B : number.A - number.B;
        }

客戶端ajax如下:

   function b4() {
        var data = { a: 1, b: 2, 'operation.add': true, 'operation.sub': false };
        ajaxOp('/api/norestful/Values/OpNumbersByNestedClass', 'POST', data);
    }

在POST請求下,複雜類型的屬性必須加類型名稱作為首碼,或者var data={a:1,b:2,operation:{add:true,sub:false}}這樣聲明,Action 參數才能獲取到數據。

 

其實說了這麼多,簡單類型綁定和複雜類型綁定在本質上沒什麼太大的區別,真正的區別在於數據綁定是通過GET請求(簡單類型的預設方式,複雜類型通過設置【FromUri】實現)還是POST請求( 複雜類型的預設方式,簡單類型通過設置【Frombody】實現)實現的,說白了就是【FromUri】特性和【FromBody】特性之間的區別。現在就講下這兩個特性內部是如何找到數據的。

【FromUri】特性

應用【FromUri】特性,Web API Action中參數將從URL中解析數據,而數據解析是通過值提供程式工廠創建值提供程式來獲取數據的,對於簡單類型,值提供程式則獲取Action參數名稱和參數值;對於複雜類型,值提供程式則獲取類型屬性名稱和屬性值。

下麵是Web API中值提供程式工廠類的代碼:

namespace System.Web.Http.ValueProviders {
    public abstract class ValueProviderFactory {
        public abstract IValueProvider GetValueProvider(HttpActionContext context);
    }
}

ValueProviderFactory通過HttpActionContext參數來選擇創建什麼樣的提供程式來解析數據。

【FromBody】特性

應用【Frombody】特性,Web API Action中參數將從請求體(Request Body),並且通過媒體類型格式化器獲取和綁定數據,在Web API框架下有4中內置的媒體格式化器,分別是:

       1:JsonMediaTypeFormatter,對應的content-type是:application/json, text/json

       2:XmlMediaTypeFormatter,對應的content-type是:XmlMediaTypeFormatter

      3:FormUrlEncodedMediaTypeFormatter,對應的content-type是:對應的content-type是:application/x-www-form-urlencoded。

      4:JQueryMvcFormUrlEncodedFormatter,對應的content-type是:對應的content-type是:application/x-www-form-urlencoded。

在預設情況下POST請求採用JQueryMvcFormUrlEncodedFormatter來解析數據的,JQueryMvcFormUrlEncodedFormatter類通過模型綁定系統利用值提供程式從URL中讀取數據,這裡的值提供程式是NameValuePairsValueProvider類,該類實現IValueProvider介面來獲取鍵值對中的數據。

 

當然,你也可以在客戶端請求時指定請求的content-type類型,這樣Web API會根據客戶端的content-type來選擇不同的媒體類型格式化器。如果客戶端採用application/json格式來傳輸數據,Web API在後臺則會採用JsonMediaTypeFormatter來解析數據。舉個慄子,上面最後一個例子更改客戶端ajax請求,Controller不變:

    function b4() {
        var data = { a: 1, b: 2, operation: { add: true, sub: false } };
        ajaxOp('/api/norestful/Values/OpNumbersByNestedClass', 'POST', JSON.stringify(data), 'application/json');
}

在下圖我們可以看到數據請求格式。這種以Json數據格式傳遞到Action來處理模型綁定,相信在MVC中無處不在吧。

而預設採用JQueryMvcFormUrlEncodedFormatter,則content-type如下圖所示:

好了,今天就到這裡吧。

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1、激活root用戶:sudo passwd root 2、安裝ftp:apt-get install vsftpd,修改配置文件/etc/vsftpd.conf write_enable=yes表明可上傳,修改完配置文件要重啟服務service vsftpd restart。 /etc/ftpus
  • FROM: http://blog.csdn.net/npy_lp/article/details/7686583 從事Linux開發的軟體工程師幾乎都使用過虛擬機軟體,如VMware workstation,一般把虛擬機軟體運行在微軟的操作系統中,把Linux操作系統(如Ubuntu)運行在虛擬機
  • 系統媽Ghost win10 64位快速安裝版 V2016年2月,更新了最新系統補丁,升級系統版本號為2016年2月份。這款累積更新補丁會取代之前的版本。本系統還附帶最常用的裝機必備軟體、QQ等。 系統下載:http://www.xitongma.com 三種激活途徑:1、利用win7、win8、w
  • 深度技術ghost win7系統 64位快速安裝版 V2016年2月,深度技術ghost win7 64位快速安裝版在不影響大多數軟體和硬體運行的前提下,已經儘可能關閉非必要服務,自動安裝AMD/Intel 雙核 CPU 驅動和優化程式,發揮新平臺的最大性能。首次登陸桌面,後臺自動判斷和執行清理目標
  • 首先定義一個字元串: string str = "abc"; 1.字元大小寫轉化 大寫:str.ToUpper(); 小寫: str.ToLower(); 2.字元和Ascii碼互相轉換 Ascii碼:byte[] b = Encoding.GetEncoding("unicode").GetByt
  • 註釋 /// <summary> /// 3.文檔註釋 /// </summary> private static void Test() { Console.WriteLine("Hello world!");// 1.單行註釋 Console.ReadKey(); /* 2.塊註釋 Consol
  • 3.0獲取介面調用憑據 ①介面說明 access_token是公眾號的全局唯一票據,公眾號調用各介面時都需使用access_token。開發者需要進行妥善保存。access_token的存儲至少要保留512個字元空間。access_token的有效期目前為2h(7200s),需定時刷新,重覆獲取將導
  • 效果 首先,我們先來準備我們需要的類 1.檢查項目類 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespa
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...