ModelBinding :用浏览器以HTTP请求方式发生数据来创建.NET对象的过程。
一、理解模型绑定。
1、模型绑定器是由IModelBinder接口定义的。下面是此接口的定义
namespace System.Web.Mvc { public interface IModelBinder { object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext); } }
2、在一个Mvc程序中,可以有多个模型绑定器,而每个绑定器可以负责一个或者多个模型类型。
3、模型绑定器负责生产动作方法的参数值,并把这个值传送给动作调用器,由动作调用器把这个值注入到目标的动作方法中去。
这通常意味着要对请求数据的某些元素进行转换,但是Mvc框架对如何获取这些数据并无如何的限制。
模型绑定的过程是通过模型绑定器来实现的,其目的是用请求中所包含的数据来创建对象。
下面是模型绑定的步骤:
(1)、检查要创建的对象的名称和类型。
(2)、通过对象名称查找请求,并找到可用数据。
(3)、根据对象类型将找到的数据值要转换的类型。
(4)、通过对象名称,对象类型,和经过处理的数据来构造目标对象。
(5)、将构造好的对象传送给动作调用器,并由动作调用器将对象注入到动作方法中。
二、ASP.Net 默认的模型绑定器(DefaultModelBinder)
1.一个应用程序可以包含多个模型绑定器,但大多数值依赖Mvc框架内置的绑定器类DefaultModelBinder。这个模型绑定器在默认情况下会按照顺序查找下面4个位置:
Request.Form,RouteData.Values,Request.QueryString,Request.Files. 这个4个地方,只要有一个地方找值,就回停止搜索。
2.当动作方法的参数为简单类型时,DefaultModelBinder会试图用System.ComponentModel.TypeDescriptor类把已经从请求数据中获取到的字符串值转换成类型的值。
3.当动作方法的参数为复杂类型时(就是不能用TypeConverter类进行转换的类型),DefaultModelBinder将用反射来获取public属性集合,如果依次进行绑定。如果还是复杂类型,将再次执行此过程,获取public属性集合,直到全部能绑定。不同的是这些属性的名称是嵌套的(HomeAddress.Line1)。
4、指定自定义前缀。在默认模型绑定器查找数据项是,可以为它指定要查找的自定义前缀。
public ActionResult Register(Person firstPerson,[Bind(Prefix=‘myPerson‘)]Person secondPerson)
5、有选择的绑定。我们可以利用Bind注解属性把模型属性包含到或排除到绑定过程。
只包含FirstName和LastName
public ActionResult Register([Bind(Include="FirstName,LastName")]Person person)
只排除IsApprove和Role
public ActionResult Register([Bind(Exclude="IsApproved,Role")]Person person)
三、绑定到XXX (DefaultModelBinder能处理具有相同名称的多个数据项的方法)
例如:
@{ ViewBag.Title = "Address"; } <h2>Addrss</h2> @Html.TextBox("movies") @Html.TextBox("movies") @Html.TextBox("movies")<input type="submit" value="提交"/>
3个Html.TextBox铺助方法生产的3个input元素,它们都会以movies所谓其name标签属性的值:
<input id="movies" name="movies" type="text" value="" /> <input id="movies" name="movies" type="text" value="" /> <input id="movies" name="movies" type="text" value="" /> <input id="movies" name="movies" type="text" value="" /> <input id="movies" name="movies" type="text" value="" /> <input id="movies" name="movies" type="text" value="" /> <input type="submit" value="提交"/>
在一个动作方法中接受多个数据项:
[HttpPost] public ActionResult Index(List<string> movies) { //... }
1.绑定到自定义类型集合
什么是自定义类型:不属于.NET体系架构中的那些已知的类型。比如我所定义的如何类型都是自定义类型。
用集合对象中的索引号作为对象各属性名的前置来生成HTML。然后在动作方法中用自定义类型的集合作为参数就可以了。
只要确保适当的生成索引值,模型绑定器就能够查找并绑定到所定义的所有数据元素。
2.绑定到非序列化索引的集合
使用任意字符串键来定义集合的数据项。:只要定义一个名为“index”的隐藏input元素,以指定数据项目的键
<h4>First Person</h4> <input type="hidden" name="index" value="firstPerson"/> First Name:@Html.TextBox("[firstPerson].FirstName") Last Name:@Html.TextBox("[FirstPerson].LastName") <h4>Second Person</h4> <input type="hidden" name="index" value="secondPerson"/> First Name:@Html.TextBox("[secondPerson].FirstName") Last Name:@Html.TextBox("[secondPerson].LastName")
该清单input元素的name添加了与隐藏的索引元素相匹配的前置([firstPerson],[secondPerson])。模型绑定器会检测这个索引,并在绑定过程中把它与数据值关联在一起。
3.绑定到字典
默认绑定器能够绑定到字典(Dictionary),但要遵循一种特殊的命名序列。
<h4>First Person</h4> <input type="hidden" name="[0].key" value="firstPerson"/> First Name:@Html.TextBox("[0].value.FirstName") Last Name:@Html.TextBox("[0].value.LastName") <h4>Second Person</h4> <input type="hidden" name="[1].key" value="secondPerson"/> First Name:@Html.TextBox("[1].value.FirstName") Last Name:@Html.TextBox("[1].value.LastName")
当绑定到Dictionary<string,Person>或者IDictionary<string,Person>时,该字典将含有从属于firstPerson和secondPerson键的2个Person对象。可以用下面的动作方法来接受数据:
[HttpPost]
public ViewResult Register(IDictionary<string,Person>people){...}
四、手动调用模型绑定。
当给动作方法定义了参数,模型绑定过程是自动执行的。我们也可以手动的控制这个过程,这样我们能够更明确的控制如何实例化模型,从何处获取数据,以及如何处理数据解析错误等。
[HttpPost] public ActionResult Index() { string name = ""; UpdateModel(name); return View(); }
UpdateModel方法以上一条语句生成的模型对象为参数,并试图用标准的绑定过程来获取其值。
手动调用模型绑定的过程的原因之一是为了支持模型对象的依赖性注入(DI)。
[HttpPost] public ActionResult Index() { //加入DI支持.但這不是加入DI支持的唯一方法 Address addressor = (Address)DependencyResolver.Current.GetServices(typeof(Address)); UpdateModel(addressor); return View(); }
1、限定绑定到特定的数据源
通过给UpdateModel方法提供IValueProvider类型的对象来指定特定的数据来源。
[HttpPost] public ActionResult Index() { //加入DI支持.但這不是加入DI支持的唯一方法 Address addressor = (Address)DependencyResolver.Current.GetServices(typeof(Address)); //限定address的值來自Request.Form中 UpdateModel(addressor, new FormValueProvider(ControllerContext)); return View(); }
4个默认的数据来源都有一个IValueProvider的实现。
数据源 | IValueProvider实现 |
Request.Form | FormValueProvider |
RouteData.Values | RouteDataValueProvider |
Request.QueryString | QueryStringValueProvider |
Request.Files | HttpFileCollectionValueProvider |
限定数据源最普遍的方式是只查找表单值。我们可以用一个更加雅致的方法实现。而不必创建FormValueProvider类实例。
[HttpPost] public ActionResult Index(FormCollection formData) { //加入DI支持.但這不是加入DI支持的唯一方法 Address addressor = (Address)DependencyResolver.Current.GetServices(typeof(Address)); //限定address的值來自Request.Form中 UpdateModel(addressor,formData); return View(); }
2.处理绑定错误。
用户难免会提供一些不能绑定到相应的模型属性的值。当我们明确调用模型绑定石,我们需要负责处理此类的如何错误。
模型绑定器是通过抛出InvalidOperationException异常来表示绑定错误。错误的细节通过ModelState特性来查看。
因此我们使用UpdateModel方法时,必须做好扑捉该异常的准备,并用ModelState把错误信息表示给用户。
//添加扑捉模型绑定过程中的异常代码 方法一 [HttpPost] public ActionResult Index(FormCollection formData) { //加入DI支持.但這不是加入DI支持的唯一方法 Address address = (Address)DependencyResolver.Current.GetServices(typeof(Address)); try { //限定address的值來自Request.Form中 UpdateModel(address, formData); }catch(InvalidOperationException er) { //基於ModelState提供用戶介面反饋 } return View(address); }
另一个方法是使用TryUpdateModel方法。如果绑定成功就返回"True",不成功就返回"false".
//扑捉模型绑定过程的异常代码 方法二 [HttpPost] public ActionResult Index(FormCollection formData) { //加入DI支持.但這不是加入DI支持的唯一方法 Address address = (Address)DependencyResolver.Current.GetServices(typeof(Address)); //try //{ // //限定address的值來自Request.Form中 // UpdateModel(address, formData); //}catch(InvalidOperationException er) //{ // //基於ModelState提供用戶介面反饋 //} if (TryUpdateModel(address, formData)) { //正常處理 } else { //基於ModelState提供用戶介面反饋 } return View(address); }
五、使用模型绑定接收文件上传
对于接收文件上传,要做的全部工作是定义一个以HttpPotedFileBase类为参数类型的动作方法。模型绑定器会用与这个上传文件相应的数据来填充它。
ASP.NET 的模型绑定(ModelBinding)