冠军

导航

ASP.NET MVC 模型绑定的 6 个建议

原文名称:6 Tips for ASP.NET MVC Model Binding

原文地址:http://odetocode.com/Blogs/scott/archive/2009/04/27/6-tips-for-asp-net-mvc-model-binding.aspx

ASP.NET MVC 中的 Model Binding 使用起来非常简单。你的 Action 方法需要数据,在传入的 HTTP 请求中携带着你需要的数据,数据可以在请求的表单数据中,还可能在你的 URL 地址本身中。通过 DefaultModelBinder,可以神奇地将表单中的数据和路由中的数据转换到对象中。Model Binder 使得你的控制器代码可以干净地从请求以及关联的环境中分离出来。

这里有一些关于在 MVC 项目中更好使用 Model Binding 的建议。

Tip#1:最好使用 Model Binding 而不是 Request.Form


如果你的 Action 像下面这样:

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create()
{
    Recipe recipe = new Recipe();
    recipe.Name = Request.Form["Name"];
    
    // ...
    
    return View();
}

就不对了。这些属性使得你的 Action 很难读而且更难以测试,Model Binder 可以帮你从 Request 和 HttpContext 中摆脱出来。比如,你可以使用 FormCollection 类型的参数来代替上面的代码:

public ActionResult Create(FormCollection values)
{
    Recipe recipe = new Recipe();
    recipe.Name = values["Name"];      
            
    // ...
            
    return View();
}

使用 FormCollection 你可以不必再深入到 Request 对象,这样,有时候你就可以使用低层次的控制了。但是,如果你的数据来自 Request.Form,或者 URL 请求参数,你可以通过 Model Binding 来完成它的魔术。

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(Recipe newRecipe)
{            
    // ...
    
    return View();
}

在这个例子中,Model Binder 将会帮你创建 newRecipe 对象,并且使用从 Request 中获得获得的数据来填充它,真的是魔术。有许多的途径允许你定制绑定的处理过程,使用白名单,黑名单,前缀,以及接口,更多的控制还允许你通过 UpdateModel 和 TryUpdateModel 方法进行,只是要注意无意的绑定。看一看 Justin Etheredge 的文章  Think Before You Bind.

Tip#2 定制 Model Binder


在 MVC 中,Model Binding 也是一个扩展点。如果默认的绑定不合适的话,你可以提供一个自定义的 Model Binder,实现自定义的 Model Binder 你需要实现接口 IModelBinder,这是仅有的一个方法,有多难吗?

public interface IModelBinder
{
    object BindModel(ControllerContext controllerContext,
                      ModelBindingContext bindingContext);
}

一旦你进入Model Binding,实际上,你将会发现这个简单的 IModelBinder 接口并没有完全描述在框架中的默认契约和负作用。如果你退回一步看一看,就会发现 Model Binder, ModelState 以及 HtmlHelper。

Scott Hanselman 在他的  “Splitting DateTime – Unit Testing ASP.NET MVC Custom Model Binders” 中给出了一个并不是演示版的 Model Binder,一个我需要提出来的细节是 Scott 的 DateTime 分离器仍然没有通过 Request.Form 来绑定,在 GetA<T> 方法中,你将会看到 Scott 使用了上下文对象的 ValueProvider 属性来获得数据,ValueProvier 表示混合了表单数据,路由数据,以及请求参数数据的数据。Scott 的例子非常棒,但是,少了一个细节:绑定中的错误。
如果默认的模型绑定器在将数据绑定到你的对象上时出现了问题,它会将错误信息和错误的数据压入到 ModelState 中,你可以检查 ModelState.IsValid 来检查绑定中的问题,使用 ModelState.AddModelError 方法可以注入你自己的错误信息。

如果你看看 Scott 文章的回应,你会看到  Sebastien Crocquesel’s  对这个问题的补丁。如果转换失败,Sebastien 的代码将会使用 ModelState.AddModelError 方法来表示错误。Controller 和 View 都会使用 ModelState 来检查绑定的问题。Controller 需要检查 ModelState,以便在将数据保存到数据库之前检查错误,而 View 需要通过 ModelState 来为用户提供验证的回应。需要注意的一点是 HtmlHelper ,你需要同时提供一个值,通过 ModelState.SetModelValue ,并且提供错误信息,通过 AddModelError,否则你将会得到一个运行时的空引用异常,下面的代码演示了这个问题。

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(FormCollection Form)
{
    // this is the wrong approach ...
    if (Form["Name"].Trim().Length == 0)
        ModelState.AddModelError("Name", "Name is required");

    return View();
}

上面的代码创建了一个模型的错误信息,但是没有提供值。也有其他的问题,但是,如果你像下面一场呈现视图,那么,就会得到一个异常。

<%= Html.TextBox("Name", Model.Name) %>

纵然你为 Model.Name 提供了一个值,HtmlHelper 也会发现错误,然后显示试图的值。如果你没有提供值,就会看到一个空引用异常。

Tip#3 通过继承来自定义 Model Binding


如果你决定实现一个自定义的 Model Binder,你可能希望通过从 DefaultModelBinder 继承来减少部分工作量,其实,最终你会发现不能通过继承 DefaultModelBinder 来达到你的目的。例如,假如你希望通过自定义的 ModelBinder 来创建某些对象,DefaultModelBinder 将会使用 Activator.CreateInstance 和 Model 的默认构造函数来创建对象,如果你的模型没有提供默认的构造函数,你可以重写 CreateModel 方法来解决这个问题。

Jimmy 有一篇关于使用 DefaultModelBinder 的派生类的帖子 “A Better Model Binder”.

Tip#4 使用注解来完成验证

Brad Wilson 在他的文章  DataAnnotations and ASP.NET MVC  中,完美地演示了一切。
我建议你仔细读一下 Brad Wilson 的文章,如果你想快一点,这里总结了一下。
.NET 3.5 SP1 带来了 System.ComponentModel.DataAnnotations 程序集,通过数据的注解和 DataAnnotationModelBinder,你可以处理大部分的服务器端验证问题,只需要简单地标注你的模型

public class Recipe
{
    [Required(ErrorMessage="We need a name for this dish.")]
    [RegularExpression("^Bacon")] 
    public string Name { get; set; }

    // ...
}


Tip#5 绑定和验证是两个步骤


绑定是从环境中获得数据,然后赋予模型对象的过程,验证是检查模型对象的数据,确认符合我们的期望。这是完全不同的操作,但是模型绑定模糊了他们的区别。如果你希望在 Model Binder 中一起完成这两步工作,是可以的,这需要准确地知道  DataAnnotationsModelBinder  做了什么,你可以看这几个例子。实际上,经常被忽略的一点是 DefaultModelBinder  如何分离绑定和验证步骤。如果只是简单属性的验证,所有你要做的就是重写 DefaultModelBinder 的 OnProperValidating 方法。

下面的几篇文章可以参考一下:

Tip#6 关于绑定的内容


前面我说过:Model Binder 可以帮你从 Request 和 HttpContext 中摆脱出来。从更加广泛的角度来说,并没有限制数据的来源,请求上下文中包含丰富的客户端信息。Scott Hanselman 的另外一篇文章演示了将用户的标识绑定到模型上。

 

综上所述

Model Binding 是美妙的魔术,所以,尽可能使用内置的奇妙功能。

posted on 2011-08-01 18:57  冠军  阅读(6000)  评论(7编辑  收藏  举报