zoukankan      html  css  js  c++  java
  • MVC中的扩展点(八)模型绑定

        MVC可以将用户提交的数据绑定到Action参数,我们将这个过程称之为模型绑定,在模型绑定中有两个关键:一个是值提供器,用于确定数据来源,另一个称为模型绑定器,用于确定如何将值绑定到特性的数据模型。

    MVC中默认的值提供器

        值提供器是一组实现了IValueProvider接口的类,MVC中的值提供其使用了标准的抽象工厂设计模式,其类图如下:

    IValueProvider

        MVC提供了四种默认的值提供器:

    FormValueProvider:表单数据,对应于ASP.NET的Request.Form集合

    QueryStringValueProvider:查询字符串,对应于ASP.NET的Request.QueryString集合

    HttpFileCollectionValueProvider:文件集合,数据来源于Request.Files集合

    RouteDataValueProvider:路由信息,对应于RouteData.Values集合

        MVC为每一个值提供器提供了一个工厂:ValueProviderFactory,ValueProviderFactoryCollection是一个值提供器工厂集合,其中的GetValueProvider方法返回与当前控制器上下文匹配的值提供器。ValueProviderFactories是一个静态类,内部封装一个ValueProviderFactoryCollection集合(Factories属性),此集合默认包含MVC中4个默认的值提供器,所以,如果我们要使用自己的值绑定器,可以通过此类的Factories属性,使用InsertItem增加新的值提供器,使用SetItem方法将某个默认值提供器替换为我们的自定义值提供器。 需要注意的是,ValueProviderFactoryCollection的GetValueProvider方法实际返回的是一个ValueProviderCollection集合,但由于此集合类同样实现了IValueProvider接口(其GetValue方法会从集合中所有的值提供器中查找符合条件的项),所以GetValueProvider的返回类型仍然为IValueProvider。

    MVC中默认的模型绑定器

        模型绑定器用于将值提供器提供的数据映射到特定的模型类型,正因为有模型绑定器的存在,才使我们可以直接使用带参数的控制器方法:绑定器可以从值提供器中获取数据,并根据控制器方法参数类型,自动实例化参数并填充数据。

        MVC中默认的模型绑定器类结构如下:

    IModelBinder

        MVC中实现四种模型绑定器:

    HttpPostedFileBaseModelBinder:用于处理HttpPostedFileBase类型

    ByteArrayModelBinder:用于处理Byte[]类型

    LinqBinaryModelBinder:用于处理Linq中的Binary类型

    DefaultModelBinder:默认绑定器,如果某个类型没有特定的绑定器,则使用此绑定器。

        ModelBinderDictionary是一个IModelBinder字典,键为类型,值为类型所对应的绑定器。DefaultBinder属性指定如果集合中不存在指定类型的绑定器时,应返回的默认绑定器,默认为DefaultModelBinder。我们可以通过此属性来指定我们自己的默认绑定器。ModelBinders是一个静态类,内部封装了ModelBinderDictionary(Binders属性),包含默认的绑定器列表。

        CustomModelBinderAttribute是一个用于指定自定义绑定器的特性的抽象基类,方法GetBinder用于返回一个绑定器实例。ModelBinderAttribute是CustomModelBinderAttribute的一个实现,它根据指定的绑定器类型,返回其实例。MVC在查找适当的绑定器时,遵循以下顺序:

    在参数上通过ModelBinderAttribute特性指定的绑定器

    在ModelBinders.Binders中注册的绑定器

    在类型上通过ModelBinderAttribute特性指定的绑定器

    默认绑定器(通常为DefaultModelBinder)

        BindAttribute特性用于指定默认绑定器在绑定时的具体行为:Exlude属性指定绑定器在填充数据时,不处理的属性列表,Include属性指定绑定器在填充数据时,只需处理的属性列表,Prefix用于指定类型的前缀,默认情况下,前缀为控制器方法中参数的名称。IsPropertyAllowed方法用于判断特定的属性是否需要被处理。

    自定义值提供器

        要实现自定义的值提供器,我们需要实现一个ValueProviderFactory和一个IValueProvider,之后将其添加到ValueProviderFactories.Factories集合中。下例实现一个CookieValueProvider,用于从Cookie中获取数据:

    1、创建一个空MVC项目

    2、实现CookieValueProviderFactory工厂及CookieValueProvider提供器

    显示行号 复制代码 CookieValueProviderFactory
    1. public class CookieValueProviderFactory : ValueProviderFactory
      
    2.  {
      
    3.     public override IValueProvider GetValueProvider(ControllerContext controllerContext)
      
    4.     {
      
    5.         return new CookieValueProvider(controllerContext.HttpContext.Request.Cookies);
      
    6.     }
      
    7. 
      
    8.     private class CookieValueProvider : IValueProvider
      
    9.     {
      
    10.         private HttpCookieCollection Cookies;
      
    11. 
      
    12.         public CookieValueProvider(HttpCookieCollection cs)
      
    13.         {
      
    14.             Cookies = cs;
      
    15.         }
      
    16. 
      
    17.         public bool ContainsPrefix(string prefix)
      
    18.         {
      
    19.             return Cookies.AllKeys.Contains(prefix);
      
    20.         }
      
    21. 
      
    22.         public ValueProviderResult GetValue(string key)
      
    23.         {
      
    24.             if (!ContainsPrefix(key))
      
    25.                 return null;
      
    26.             string value = Cookies[key].Value;
      
    27.             return new ValueProviderResult(value,value, CultureInfo.CurrentCulture );
      
    28.         }
      
    29.     }
      
    30. }
      
    31. 
      
     

    3、在Application_Start中注册自定义值提供器工厂

    ValueProviderFactories.Factories.Insert(0, new CookieValueProviderFactory());

    4、实现一个测试控制器,HomeController:

    显示行号 复制代码 HomeController
    1. public class HomeController : Controller
      
    2. {
      
    3.     public ActionResult Index(DateTime? lastTime)
      
    4.     {
      
    5.         if (!Request.Cookies.AllKeys.Contains("lastTime"))
      
    6.         {
      
    7.             Response.Cookies.Add(new HttpCookie("lastTime", DateTime.Now.ToString()));
      
    8.         }
      
    9.         return Content(lastTime == null ? String.Empty : lastTime.ToString());
      
    10.     }
      
    11. }
      
    12. 
      

        注意,参数类型为DateTime?可空类型,这样可防止默认绑定器在没有找到对应值时报错。测试时,首次运行,由于Cookie中没有lastTime项,所以页面为空,刷新一次页面,此时页面将显示上一次访问的时间。

    自定义模型绑定器

        通过实现IModelBinder接口可实现自定义模型绑定器,下例将实现一个XDocument类型的绑定器:

    1、创建一个空MVC项目

    2、实现XDocumentBinder

    显示行号 复制代码 XDocumentBinder
    1. public class XDocumentBinder : IModelBinder
      
    2. {
      
    3.     public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
      
    4.     {
      
    5.         string key = bindingContext.ModelName;
      
    6.         ValueProviderResult vpr = bindingContext.ValueProvider.GetValue(key);
      
    7.         if (vpr !=null && vpr.RawValue != null && !String.IsNullOrEmpty(vpr.AttemptedValue))
      
    8.         {
      
    9.             //在模型状态中保存尝试值
      
    10.             bindingContext.ModelState.SetModelValue(key, vpr);
      
    11.             string tempString = ((String[])vpr.RawValue)[0];
      
    12.             XDocument xml = null;
      
    13.             try
      
    14.             {
      
    15.                 xml = XDocument.Parse(tempString);
      
    16.             }
      
    17.             catch (XmlException)
      
    18.             {
      
    19.                 //无法解析XML文本,则设置模型错误状态
      
    20.                 bindingContext.ModelState.AddModelError(key, "Not valid XML");
      
    21.                 return null;
      
    22.             }
      
    23. 
      
    24.             //如果模型已经存在,则替换
      
    25.             XDocument existingModel = bindingContext.Model as XDocument;
      
    26.             if (existingModel != null)
      
    27.             {
      
    28.                 if (existingModel.Root != null)
      
    29.                 {
      
    30.                     existingModel.Root.ReplaceWith(xml.Root);
      
    31.                 }
      
    32.                 else
      
    33.                 {
      
    34.                     existingModel.Add(xml.Root);
      
    35.                 }
      
    36.                 return existingModel;
      
    37.             }
      
    38.             else
      
    39.             {
      
    40.                 return xml;
      
    41.             }
      
    42.         }
      
    43.         return null;
      
    44.     }
      
    45. }
      
    46. 
      

    3、在Application_Start中注册XDocumentBinder

    ModelBinders.Binders.Add(typeof(XDocument), new XDocumentBinder());

    4、创建用于测试的HomeController及其View

    显示行号 复制代码 HomeController
    1. public class HomeController : Controller
      
    2. {
      
    3.     [ValidateInput(false)]
      
    4.     public ActionResult Index(XDocument xml)
      
    5.     {
      
    6.            
      
    7.         if (xml == null)
      
    8.         {
      
    9.             return View();
      
    10.         }
      
    11.         else
      
    12.         {
      
    13.             Response.Clear();
      
    14.             return Content(xml.ToString(), "application/xml");
      
    15.         }
      
    16.     }
      
    17. }
      
    18. 
      
    显示行号 复制代码 Index.aspx
    1.     <div>
      
    2.     <%using (Html.BeginForm())
      
    3.       {%>
      
    4.       <%= Html.ValidationSummary() %>
      
    5.        <%=Html.TextArea("xml", new { Rows=20, Cols=50 })%> 
      
    6.        <input type="submit" value ="submit" />
      
    7.      <% } %>
      
    8.     </div>
      
    9. 
      

        关于注册XDocumentBinder,我们可以通过ModelBinders.Binders将某个类型映射到绑定器,也可以通过在参数上、类型上通过ModelBinderAttribute特性来指定。

        本例中Index方法上,我们指定了[ValidateInput(false)]特性,用于跳过输入验证,这是因为默认设置下,MVC不允许提交XML类型的数据。

    默认绑定器扩展

        DefaultModelBinder默认绑定器通过Activator.CreateInstance来实例化模型类型,所以我们的模型必须要用无参构造器。要解除此限制,我们可以使用DI技术对默认绑定器进行扩展:从DefaultModelBinder继承一个新类,并重写CreateModel方法,使用DI来实例化模型。最后通过 ModelBinders.Binders.DefaultBinder属性指定我们的自定义默认绑定器。

        关于DI,在MVC中的扩展点(三)控制器工厂中有所涉及,有兴趣的朋友可参照该示例来实现自定义默认绑定器。

    源代码下载

  • 相关阅读:
    python 有关datetime时间日期 以及时间戳转换
    开园杂记
    vue beforeRouteEnter 注意点
    html 头像裁剪框
    window server服务器配置ftp服务
    js 使用 delete 删除对象的属性
    win 自带的截屏工具
    idea 配置新建类自动加注解
    mysql varchar 使用唯一索引时无法区分大小写 可以使用varbinary
    在 laradock 环境中使用 laravel-swoole 加速你的 laravel 应用
  • 原文地址:https://www.cnblogs.com/xfrog/p/1936699.html
Copyright © 2011-2022 走看看