MVC教程--Action参数绑定ModelBinding详解--内置的Model绑定器

MVC教程--Action参数绑定ModelBinding详解--内置的Model绑
定器
了解了什么是MVC的Model绑定(Model Binding)之后我们来看看MVC内置为我们提供的Model绑定器。我们可以把Model绑定按类型复杂程度和类型分为简单类型,复杂类型,数组和集合。我之前的⽂章有详细介绍MVC中的Controller是怎么接收输⼊值的,请看:
当 MVC框架接收到⼀个请求是,它会提取相应的路由信息,其中包括⽤controller名,action名,以及⼀些⾃定义的变量。MVC⾃带的⼀个 Action Inovker名ControllerActionInvoker要调⽤⼀个Action⽅法之前会利⽤model binder(model绑定器)来⽣成调⽤Action所需要的参数数据或对象(简单值或对象)。model binder(model绑定器)要实现⼀个接⼝IModelBinder,如下:
1. namespace System.Web.Mvc {
2.
3. public interface IModelBinder {
4. object BindModel(ControllerContext controllerContext,
5. ModelBindingContext bindingContext);
6. }
7. }
在⼀个MVC的程序中可以存在多个Model Binder,正如可以存在多个视图引擎⼀样。每⼀个Model Binder可以负责绑定⼀个或多个Model类型。当Action Invoker要调⽤⼀个Action⽅法是时,它会根据其参数的类型到相应的Model Binder来绑定相应的类型。以上⾯的HomeController的Index为例来说明⼀下:
我们请求 Url:/Home/Index/123,时Action Invoker就会检查Index⽅法的参数,看到Index⽅法需要⼀个类型为int的参数,因此它不会到⼀个负责绑定int类型的Model Binder并调⽤它的BindModel。这时Model Binder就负责根据请求上下⽂信息提供⼀个int类型的值给调⽤Index⽅法⽤。这就意味了提供参数的值是通过请求数据信息,并在适当的时候会有类型转换,这些数据信息可能是表单数据或者是查询字符串的值(Form或Query String)。
注意:这些数据信息的来源MVC框架并没有限制只是从表单或查询字符串的值中获取。
虽然我们能够继承接⼝IModelBinder⾃定义⼀个ModelBinder来绑定我们⾃定义的类型,但是在实践开
发过程⽤得最多还是MVC内置的Binder类DefaultModelBinder。当Action Invoker不到⼀个⾃定义的Binder来绑定⼀个指定类型时候,就会使⽤这个内置的Binder类DefaultModelBinder。默认情况这个DefaultModelBinder会在如下的集合中查参数名相对应的数据信息。
1、Request.Form
2、RouteData.Values
3、Request.QueryString
4、Request.Files
查的顺序是从1-4的顺序,⽐如DefaultModelBinder要查⼀个我们Index⽅法需要的参数id的值,将按以下顺序查:
1. Request.Form["id"]
2. RouteData.Values["id"]
3. Request.QueryString["id"]
4. Request.Files["id"]
如果在前⾯到⼀个名为id的值,将不会继续往下。⽐如在1中Request.Form["id"]到了id的值,那么2,3,4根本不会进⾏查。
2.1、Model绑定简单类型
这⾥说的简单类型也就是C#内置的类型⽐如:int,string,DateTime,decimal等等。上⾯绑定就是⽤绑定简单类型int。MVC要绑定这些简单类型很简单,不⽤你做特别的处理。MVC框架⾃动根据你传⼊的值进⾏类型转换,⽐如当Action参数为int,⽽你传⼊值能够正确转换成int。下⾯我们来做⼀个试验,在上⾯的例⼦中我们的路由配置的id参数是可选的。下⾯我们在浏览器输⼊"/Home/Index/",也就是id不输⼊值,看看会出现什么效果:
对于 “MVCModelBindingDemo.Controllers.HomeController”中⽅法 “System.Web.Mvc.ActionResult Index(Int32)”的不可以为 null 的类型“System.Int32”的参数“id”,参数字典包含⼀个 null 项。可选参数必须为引⽤类型、可以为 null 的类型或声明为可选参数。
参数名: parameters
可看到我们没有提供id值就会报上⾯错。因为Action Index要求⼀个类型为Int32的参数,url中没有这
个参数的值,默认就是为null。要避免这样的错误我们⼀般有两种做法:
1、在路由配置中指定默认值,正如:
1. public static void RegisterRoutes(RouteCollection routes)
2. {
3. routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
4.
5. routes.MapRoute(
6. name:"Default",
7. url:"{controller}/{action}/{id}",
8. defaults:new{ controller ="Home", action ="Index", id =999}
9. );
10. }
2、在Action参数中指定默认值,如下:
1. public class HomeController : Controller
2. {
3. public ActionResult Index(int id=999)
4. {
5. ViewBag.ID = id;
6. return View();
保健食品质量与管理
7. }
8. }
2.2、Model绑定复杂类型
传承进化
复杂类型⼀般是⾃⼰定义的⼀些类,⽐如我们的Action参数的类型是⼀个领域对象,或者是⼀个数据库的Model。当Action参数类型是⼀个复杂的类型时就不能像简单类型那样直接字符串解析成int这样类型转换,DefaultModelBinder会⽤到依次访问这个Model的public公有属性,并给它赋值上相应的值。为了展⽰这个过程我们把之前的HomeController改成如下:
1. public class HomeController : Controller {
2. private Person[] personData ={
3. new Person {PersonId =1, FirstName ="Adam", LastName ="Freeman",
4. Role = Role.Admin},
5. new Person {PersonId =2, FirstName ="Steven", LastName ="Sanderson",
6. Role = Role.Admin},
7. new Person {PersonId =3, FirstName ="Jacqui", LastName ="Griffyth",
8. Role = Role.User},
9. new Person {PersonId =4, FirstName ="John", LastName ="Smith",
10. Role = Role.User},
11. new Person {PersonId =5, FirstName ="Anne", LastName ="Jones",
12. Role = Role.Guest}
13. };
14.
15. public ActionResult Index(int id =1){
16. Person dataItem = personData.Where(p => p.PersonId == id).First();
17. return View(dataItem);
18. }
19.
20. public ActionResult CreatePerson(){
21. return View(new Person());
材料科学与工程学报22. }
23.
24. [HttpPost]
25. public ActionResult CreatePerson(Person model){
26. return View("Index", model);
27. }
28. }
第⼀个CreatePerson⽅法没有任何的参数,返回视图传了⼀个Person对象给视图/Views/Home/CreatePerson.cshtml。下⾯是CreatePerson.cshtml内容:
1. @model MVCModelBindingDemo.Models.Person
2. @{
3. ViewBag.Title ="CreatePerson";
4. }
5. <h2>Create Person</h2>
6. @using(Html.BeginForm()) {
7. <div>@Html.LabelFor(m => m.PersonId)@Html.EditorFor(m=>m.PersonId)</div>
8. <div>@Html.LabelFor(m => m.FirstName)@Html.EditorFor(m=>m.FirstName)</div>
海安紫石中学9. <div>@Html.LabelFor(m => m.LastName)@Html.EditorFor(m=>m.LastName)</div>
10. <div>@Html.LabelFor(m => m.Role)@Html.EditorFor(m=>m.Role)</div>
11. <button type="submit">Submit</button>
12. }
本视图采⽤了Model强类型视图,对应Model类型是MVCModelBindingDemo.Models.Person。
在/Content/Site.css尾部加⼊以下css样式:
1. label {display: inline-block;width:100px;font-weight:bold;margin:5px;}
2. form label {float: left;}
3. -box {float: left;margin:5px;}
4. button[type=submit]{margin-top:5px;float: left;clear: left;}
5. form div {clear: both;}
我们访问地址:/Home/CreatePerson时将会出现的页⾯中输⼊我们想要输⼊的内容,如下图:
我们最后点击提交Submit按钮,在带有[HttpPost]的CreatePerson⽅法打⼀个断点看看Action的参数model对象的各字段的值:
可以看到每⼀个⽂本框输⼊的值正好就到了Person对象的对应的字段中去了。当我们⽤Post提交表单请求的时候就会提交到带有特性HttpPost的 CreatePerson⽅法,内置的model binder发现这个Action的参数是⼀个类Person,它就会依次处理这个类的属性,并在请求的上下问中到相应的数据并给属性赋值。⽐如当发现 Person的有⼀个字段为PersonId时,它就会在请求数据中到⼀个名为PersonId的值并转换成Int之后赋值给这个字段。如果 Action参数的类的字段⼜是⼀个类,那么⼜会重复最外层的步骤,依次绑定它的每个字段的值。下⾯我们就来演⽰⼀下这种情况。
⾸先,我们定义⼀个地址类Address:
1. public class Address {
2. public string Line1 { get; set;}
3. public string Line2 { get; set;}
4. public string City { get; set;}
5. public string PostalCode { get; set;}
6. public string Country { get; set;}
7. }
然后,我们把给Person加上⼀个Adress类型的属性HomeAdres:
1. public class Person
2. {
3. public int PersonId { get; set;}
4. public string FirstName { get; set;}
5. public string LastName { get; set;}
6. public Role Role { get; set;}
7. public Address HomeAddress { get; set;}
8. }
接下来,我们修改⼀个CreatePerson.cshtml视图:
1. @model MVCModelBindingDemo.Models.Person
2. @{
3. ViewBag.Title ="CreatePerson";
4. }
5. <h2>Create Person</h2>
6. @using (Html.BeginForm())
7. {
8. <div>@Html.LabelFor(m => m.PersonId)@Html.EditorFor(m => m.PersonId)</div>
9. <div>@Html.LabelFor(m => m.FirstName)@Html.EditorFor(m => m.FirstName)</div>
10. <div>@Html.LabelFor(m => m.LastName)@Html.EditorFor(m => m.LastName)</div>
11. <div>@Html.LabelFor(m => m.Role)@Html.EditorFor(m => m.Role)</div>
12. <div>
13. @Html.LabelFor(m => m.HomeAddress.City)
14. @Html.EditorFor(m=> m.HomeAddress.City)
15. </div>
16. <div>
17. @Html.LabelFor(m => m.HomeAddress.Country)
18. @Html.EditorFor(m=> m.HomeAddress.Country)
19. </div>
20. <div>
21. <button type="submit">Submit</button>
22. </div>
23. }
页⾯录⼊数据如下图:
提交之后,打断点监控model变量如下图:
可以看到页⾯录⼊的City和Country的值⾃动到了model的HomeAdress对象中去了。我们最后来看看输⼊页⾯City和County部分的html 是什么样的:
1. <div>
2. <label for="HomeAddress_City">City</label>
3. <input class="text-box single-line"id="HomeAddress_City"name="HomeAddress.City"type="text"value=""/>
4. </div>
5. <div>
6. <label for="HomeAddress_Country">Country</label>
7. <input class="text-box single-line"id="HomeAddress_Country"name="HomeAddress.Country"type="text"value=""/>
8. </div>医疗纠纷论文
从上⾯看input的name属性都是"HomeAdress.“开头,也就是默认name的命名规则为”属性名.字段名“的格式。这样MVC的Model Binder 才能利⽤反射对应到这个值是哪⼀个属性的的哪⼀个字段。
2.3、利⽤Bind特性类有选择的绑定类的字段属性
泵效率下⾯我们举⼀个实例对Bind特性的使⽤:
1. [HttpPost]
2. public ActionResult CreatePerson([Bind(Prefix ="HomeAddress", Exclude ="PersonId", Include =

本文发布于:2024-09-24 00:25:22,感谢您对本站的认可!

本文链接:https://www.17tex.com/xueshu/453321.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:类型   参数   绑定   数据   信息   属性   请求   表单
留言与评论(共有 0 条评论)
   
验证码:
Copyright ©2019-2024 Comsenz Inc.Powered by © 易纺专利技术学习网 豫ICP备2022007602号 豫公网安备41160202000603 站长QQ:729038198 关于我们 投诉建议