一.前言
下面我們將開始學習模型綁定,通過下面的知識我們將能夠理解ASP.NET MVC模型的模型綁定器是如何將http請求中的數據轉換成模型的,其中我們重點講述的是表單數據。
二.正文
1.簡單類型綁定
學過一定ASP.NET MVC都會為這個特點所驕傲,就是能夠將表單中與同名的參數映射,這相比操作ASP.NET控件來獲取值輕便了許多,但是正如上面所說的那樣要同名(大小寫不區分),下面我們會講述如何自己去指定。
首先我們在HomeController(如果不存在則創建)中獲取表單中的值並顯示:
1 namespace MvcStudy.Controllers 2 { 3 public class HomeController : Controller 4 { 5 public ActionResult Index() 6 { 7 return View(); 8 } 9 10 [HttpPost] 11 public ActionResult Index(String person) 12 { 13 TempData["msg"] = person ?? ""; 14 return View(); 15 } 16 } 17 }
接着我們在Views/Home/Index.cshtml中寫入如下代碼:
1 @{ 2 ViewBag.Title = "Index"; 3 } 4 5 @using (Html.BeginForm()) 6 { 7 <input type="text" name="perSon" /> 8 <input type="submit" value="submit" /> 9 } 10 11 @if (TempData.ContainsKey("msg")) 12 { 13 <p> 14 @TempData["msg"].ToString() 15 </p> 16 }
這里我們有一個name為perSon的輸入框,下面我們在提交后還輸出了這個值。下面讀者可以嘗試輸入一個值,可以看到下面會對應的輸出。但是這個時候我們將name為perSon的輸入框改成persons就不會顯示了。理由很簡單,因為名稱不同了,但是通過Bind注解屬性可以解決這個問題,下面我們打開HomeController並修改代碼:
1 [HttpPost] 2 public ActionResult Index([Bind(Prefix="persons")]String person) 3 { 4 TempData["msg"] = person ?? ""; 5 return View(); 6 }
這里我們通過Bind的Prefix修改了person的前綴,然后重新編譯,我們可以發現又顯示了。這樣以后我們並不需要名稱都一樣了。
上面介紹的只是對於一個簡單類型的情況,當然多的可以以此類推,但如果是數組呢?得益於ASP.NET MVC的默認模型綁定我們只要重復同一個name多次,就可以簡單的形成了數組了比如我們將Index.cshtml改成如下代碼:
1 @using (Html.BeginForm()) 2 { 3 <input type="text" name="persons" /> 4 <input type="text" name="persons" /> 5 <input type="text" name="persons" /> 6 <input type="text" name="persons" /> 7 <input type="text" name="persons" /> 8 <input type="text" name="persons" /> 9 <input type="submit" value="submit" /> 10 } 11 12 @if (TempData.ContainsKey("msg")) 13 { 14 <p> 15 @TempData["msg"].ToString() 16 </p> 17 }
作為演示我們只是簡單的將輸入框復制了幾遍,下面我們修改HomeController的代碼:
1 [HttpPost] 2 public ActionResult Index([Bind(Prefix="persons")]IList<String> person) 3 { 4 TempData["msg"] = String.Join(";", person); 5 return View(); 6 }
通過上面代碼我們可以看到即使是數組一樣也是可以使用Bind注解屬性的,同時類型也改成了IList<String>,下面我們通過String的Join方法將這個數組拼接成一個數組,中間以分號分割。讀者這個時候可以重新編譯然后嘗試,最后的效果如下所示:
2.復合類型綁定
很多實際的情況我們都會使用一個復合類型取代多個簡單類型,那么問題就來了,ASP.NET MVC是如何綁定呢?對於一個復合類型,還是會按照名稱映射到復合屬性中對應的屬性,但是好奇的人一定會使用Bind注解屬性,想看看會發生什么事情,那么我們下面就 模擬一下,首先我們新建一個簡單的Person類:
1 namespace MvcStudy.Models 2 { 3 public class Person 4 { 5 public String FirstName { get; set; } 6 public String LastName { get; set; } 7 public Address HomeAddress { get; set; } 8 } 9 }
還有一個Address類(后面需要使用):
1 namespace MvcStudy.Models 2 { 3 public class Address 4 { 5 public String Line1 { get; set; } 6 public String Line2 { get; set; } 7 } 8 }
接着我們修改Index.cshtml將其中的表單的輸入框於Person對應起來:
1 @using (Html.BeginForm()) 2 { 3 <input type="text" name="FirstName" /> 4 <input type="text" name="LastName" /> 5 <input type="submit" value="submit" /> 6 } 7 8 @if (TempData.ContainsKey("msg")) 9 { 10 <p> 11 @TempData["msg"].ToString() 12 </p> 13 }
與之對應的還要修改HomeController中的代碼:
1 namespace MvcStudy.Controllers 2 { 3 public class HomeController : Controller 4 { 5 public ActionResult Index() 6 { 7 return View(); 8 } 9 10 [HttpPost] 11 public ActionResult Index(Person person) 12 { 13 TempData["msg"] = person.FirstName + person.LastName; 14 return View(); 15 } 16 } 17 }
我們先不使用Bind注解屬性,可以發現最后的輸出是正確的,然后我們加上Bind注解屬性:
1 [HttpPost] 2 public ActionResult Index([Bind(Prefix="p")]Person person) 3 { 4 TempData["msg"] = person.FirstName + person.LastName; 5 return View(); 6 }
重新編譯,我們可以發現最后不會輸出我們預想的結果了,而且還報異常了。這個時候我們考慮這個Prefix在這里起什么作用了,字面上看是前綴的意思,但是我們發現在簡單類型中直接就是表示這個簡單的類型將會從表單的Key為指定的值中獲取。但是這里是類,所以意義就不同了,而是在類中的所有屬性前加上了一個p,同時后面還追加一個點。下面我們修改Index.cshtml代碼:
1 @using (Html.BeginForm()) 2 { 3 <input type="text" name="p.FirstName" /> 4 <input type="text" name="p.LastName" /> 5 <input type="submit" value="submit" /> 6 } 7 8 @if (TempData.ContainsKey("msg")) 9 { 10 <p> 11 @TempData["msg"].ToString() 12 </p> 13 }
然后我們刷新頁面,重新測試,可以發現值又輸出了。但是我們可以發現這個類中還有一個HomeAddress屬性,但是這個屬性是一個Address類,這個name怎么表示呢?你可能會認為是Address中屬性的名稱,但事實並不是這樣的,因為而是跟上面一樣通過點來分割,我們修改Index.cshtml代碼如下:
1 @using (Html.BeginForm()) 2 { 3 <input type="text" name="p.FirstName" /> 4 <input type="text" name="p.LastName" /> 5 <input type="text" name="p.HomeAddress.Line1" /> 6 <input type="text" name="p.HomeAddress.Line2" /> 7 <input type="submit" value="submit" /> 8 } 9 10 @if (TempData.ContainsKey("msg")) 11 { 12 <p> 13 @TempData["msg"].ToString() 14 </p> 15 }
上面我們看到我們通過p.HomeAddress.Line1將Person的HomeAddress傳遞給ASP.NET MVC當然我們還要修改HomeController不然看不到最終的效果:
1 [HttpPost] 2 public ActionResult Index([Bind(Prefix="p")]Person person) 3 { 4 TempData["msg"] = person.HomeAddress.Line1 + person.HomeAddress.Line2; 5 return View(); 6 }
最后我們可以測試了,大家一定都能看到輸出了吧。即便是類,也會出現數組的情況,那么我們如何呢?當然還是利用這個點來分割,只是前面不是名稱而是索引值,下面我們當然還要修改Index.cshtml:
1 @using (Html.BeginForm()) 2 { 3 <input type="text" name="[0].FirstName" /> 4 <input type="text" name="[0].LastName" /> 5 <input type="text" name="[1].FirstName" /> 6 <input type="text" name="[1].LastName" /> 7 <input type="submit" value="submit" /> 8 } 9 10 @if (TempData.ContainsKey("msg")) 11 { 12 <p> 13 @TempData["msg"].ToString() 14 </p> 15 }
這里我們用的[]來作為第一個分隔符,里面的值就是索引值,下面我們還要修改HomeController,這樣才能輸出結果:
1 [HttpPost] 2 public ActionResult Index(List<Person> person) 3 { 4 TempData["msg"] = person.Count.ToString(); 5 return View(); 6 }
為了方便這里只輸出了有多少個項,然后我們重新編譯,刷新頁面就可以發現輸出的永遠是2,無論你輸入不輸入值,因為http會將那四個輸入框全部發送到服務端。如果你是使用js動態生成,那么麻煩就來了,就是你要負責維持這些索引,幸好ASP.NET MVC想到這點了,所以還提供了另一個解決方式,我們可以將上面的Index.cshtml改成如下代碼:
1 @using (Html.BeginForm()) 2 { 3 <input type="hidden" name="index" value="first" /> 4 <input type="text" name="[first].FirstName" /> 5 <input type="text" name="[first].LastName" /> 6 <input type="hidden" name="index" value="asd" /> 7 <input type="text" name="[asd].FirstName" /> 8 <input type="text" name="[asd].LastName" /> 9 <input type="submit" value="submit" /> 10 } 11 12 @if (TempData.ContainsKey("msg")) 13 { 14 <p> 15 @TempData["msg"].ToString() 16 </p> 17 }
這里唯一的區別就是需要一個name為index的表單用來保存索引的名稱,這樣你只要保證索引的名稱不會重復即可,但是對於動態性更強的情況來說這些還是太死板,如果可以采用字典就好了,當然我們的願望也一樣可以實現,我們只需要使用name為[索引值].key保存保存key,而利用name為[索引值].value保存值即可(value可以為類),比如我們修改Index.cshtml代碼:
1 @using (Html.BeginForm()) 2 { 3 <input type="text" name="[0].key" /> 4 <input type="text" name="[0].Value" /> 5 <input type="text" name="[1].key" /> 6 <input type="text" name="[1].Value" /> 7 <input type="submit" value="submit" /> 8 } 9 10 @if (TempData.ContainsKey("msg")) 11 { 12 <p> 13 @TempData["msg"].ToString() 14 </p> 15 }
接着我們修改HomeController接收這個字典:
1 [HttpPost] 2 public ActionResult Index(Dictionary<string,string> person) 3 { 4 TempData["msg"] = string.Join(";", person.Keys); 5 return View(); 6 }
然后我們重新編譯,並刷新頁面,就可以看到我們輸入的key提交之后都輸出了