在WebForm,獲取提交表單的值一般都是Request.Form["Title"]這樣的方式。在MVC中,提供了模型綁定機制。讓后台獲取表單或Url中的參數變得更加簡單。
一、基本模型綁定
你可以直接在參數中用字符串,整型變量,實體或者是List<實體>的方式獲取表單提交的參數。
參數中的這些東西都是與表單中的Html控件的name屬性一一對應的。
public ActionResult PersonAdd(int Id) { return View(); }
例如以上代碼,它能夠匹配Url中的Id參數。如以下兩種方法Id都能夠匹配到1
http://localhost/Home/PersonAdd/1
http://localhost/Home/PersonAdd?Id=1
在例如如下代碼:
public ActionResult PersonAdd(string Name) { return View(); }
它能夠匹配到表單中提交的張三:
<input type="text" name="Name" value="張三" />
也能夠匹配到Get請求的路徑參數:
http://localhost/Home/PersonAdd?Name=張三
如果是用實體,則會檢查該實體的屬性名與表單中name屬性中對應的標簽的值。
例如有如下實體:
public class Person_Model { public int Id { get; set; } public string Name { get; set; } }
在Controller中的參數填寫如下:
[HttpPost] public ActionResult PersonAdd(Person_Model model) { if (ModelState.IsValid) //此處僅作演示,不考慮安全性 { //插入數據庫省略 return Redirect("/Home/PersonManager"); } return View(); }
這樣的話,模型綁定器會自動檢查該實體的屬性與Name一一對應的標簽並綁定。如下表單的值將被綁定到model實體的屬性中。
<input type="hidden" name="Id" value="1" /> <input type="text" name="Name" value="張三" />
二、顯式模型綁定
UpdateModel與TryUpdateModel都用於顯示模型綁定。如果綁定期間出現錯誤或者模型是無效的。
UpdateModel將拋出一個異常。因此UpdateModel要用try catch語句塊包起來,而TryUpdateModel不會拋出異常,而是返回一個布爾類型的值,true表示綁定成功,false表示綁定失敗。如:
[HttpPost] public ActionResult PersonAdd() { Person_Model model = new Person_Model(); try { UpdateModel(model); //插入數據庫 return Redirect("/Home/PersonManager"); } catch { return View(model); } }
TruUpdateModel:
[HttpPost] public ActionResult PersonAdd() { Person_Model model = new Person_Model(); if (TryUpdateModel(model)) { //插入數據庫 return Redirect("/Home/PersonManager"); } else { return View(model); } }
另外,模型綁定還有一個模型狀態,模型綁定器一斤模型中的每一個值在模型狀態中都有相應的一條記錄。可以隨時查看綁定狀態。如:
[HttpPost] public ActionResult PersonAdd() { Person_Model model = new Person_Model(); TryUpdateModel(model); if (ModelState.IsValid) { //if(ModelState.IsValidField("Name")) //插入數據庫 return Redirect("/Home/PersonManager"); } else { return View(model); } }
三、安全問題:重復提交
假設有如下實體:
public class Comment { public int Id { get; set; } //評論者姓名 public string Name { get; set; } //評論內容 public string Content { get; set; } //是否已審核 public bool Approved { get; set; } }
在Controller中:
public ActionResult CommentAdd(Comment com) { if (ModelState.IsValid) { //添加數據庫 return Redirect("/Home/CommentManager"); } else { return View(com); } }
在以上代碼中,如果有惡意用戶在表單數據中添加"Approved=true"來干預表單的提交,那么該評論將是默認就通過審核的。這時候我們可以使用Bind特性來防御重復提交攻擊。
白名單:
[Bind(Include="Name,Content")] //白名單,只綁定這兩個屬性 [Bind(Exclude="Id,Approved")] //黑名單,不綁定這兩個屬性
Bind特性可以應用於參數左側也可以應用於實體Model類的頂部,應用於實體Modle的頂部則是對所有該實體綁定有效,而應用於參數左側則只是對該action中的請求有效。
如:
public ActionResult CommentAdd([Bind(Exclude="Approved")]Comment com) { if (ModelState.IsValid) { //添加數據庫 return Redirect("/Home/CommentManager"); } else { return View(com); } }
另外,UpdateModel與TryUpdateModel也有一個重載版本來接收一個綁定列表:
UpdateModel(com, "", new string[] { "Id", "Name", "Content" });
最后,還有一種就是視圖模型,即另外在定義一個模型來專供視圖使用,僅僅包括需要綁定的屬性。
另外,如果兩個類有相同的Name屬性,要同時綁定,區分HTML可以這樣寫:
<p>客戶名稱: <input type="text" name="customer.Name" style="width: 300px" /></p> <p>銷售員名稱: <input type="text" name="salesman.Name" style="width: 300px" /></p>
三、模型綁定原理
在ASP.NET MVC中,用戶請求道服務器的數據將被包裝為Model數據對象,這個數據對象通常也被View用來提供顯示的數據。在ASP.NET MVC中,提供了非常靈活的Model綁定機制,通過IModelBinder借口,定義了綁定Model數據的約定,並提供了一個接口的默認實現DefaultModelBinder。在大多數情況下,僅僅通過DefaultModelBinder就可以完成Model的綁定。
如果需要的話,也可以自定義一個IModelBinder的實現,完成特定類型的Model綁定。
public interface IModelBinder { object BindModel(ControllerContext controllerContext,ModelBindContext bindingContext); }
1、綁定Model
默認情況下,ASP.NET MVC使用DefaultModelBinder來綁定Model的數據。在傳遞Action參數的時候,ASP.NET MVC按照如下順序查找匹配的數據:
- form表單中的數據;
- RouteData中的數據;
- QueryString中的數據;
2、簡單參數和復雜參數
如果Action方法的參數類型是值類型和字符串類型,那么DefaultModelBinder將尋找與Action參數名稱匹配的參數,如果沒有對應的參數,那么Action的參數將試圖賦予空引用。因此,對於簡單類型的參數來說,參數的類型應該是可空的。
多數情況下,我們會通過一個Model對象來處理復雜的參數,DefaultModelBinder會遍歷Model對象的屬性來綁定參數。
如果不希望DefaultModelBinder對某個參數進行綁定,可以通過BindAttribute進行說明,其中定義了三個屬性:
- Include表示需要綁定的屬性,各個屬性之間以逗號進行分隔。
- Exclude表示不需要綁定的屬性,各個屬性之前以逗號分隔。
- Prefix表示請求參數的前綴。
這些標簽可以定義在Model上,說明在參數綁定過程中需要綁定的屬性或者不需要綁定的屬性,如:
[Bind(Include = "Name,Birthday")] public class Person { public int Id { get; set; } public string Name { get; set; } public DateTime Birthday{ get; set; } }
在UpdateModel方法中,指定包含的屬性和不包含的屬性。
UpdateModel( person, //Model "person", //Prefix new[] { "Id","Name" }, //Include new [] { "Birthday" } //Exclude );
