• ASP.NET MVC内置的数据注解


           验证是ASP.NET MVC开发中一个非常重要的环节,包括客户端和服务端验证。幸好,MVC提供了非常简便的数据注解(Data Annotations)来帮助我们进行这项工作。

    1.验证性的数据注解

           MVC本身内置了一些常用的数据注解,像是Required,DisplayName等等,我会在下面一一讲解。

           最常用的就是Required,像是下面这样:

         

          使用Required可以指定错误消息:

    [Required(ErrorMessage = "First Name is required")]
    public string FirstName { get; set; }

          使用Required是不够的,我们还需要规定用户的输入限制,像是字符串,就常有长度的限制,这时我们就可以利用StringLength。像是这样:

    [Required(ErrorMessage = "First Name is required")]
    [StringLength(160)]
    public string FirstName{ get; set;}

          它规定了我们输入的最大长度是160个字符。如果想要规定最小长度,我们可以这样写:

    [Required(ErrorMessage = "First Name is required")]
    [StringLength(160, MinimumLength = 3)]
    public string FirstName { get; set; }

          实际的效果如图:


          字符串的要求是长度,而数字的要求则是数据范围范围:

     [Range(0.01, 100.0, ErrorMessage = "Price must be between 0.01 and 100.0")]
     public decimal Price { get; set; }

          Range()是一个双闭区间,就是说,包含0.01和100.0。

          最后一个要讲的,就是RegularExpression。

         我们可以这样使用:

    [Required(ErrorMessage = "Email is required")]
    [RegularExpression(@"[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+[A-Za-z]{2,4}", ErrorMessage = "Email is not valid")]
    [DataType(DataType.EmailAddress)]
    public string Email { get; set; }

        通过上面,我们可以知道,所有用于验证用的数据注解都可以自定义自己的错误消息。

    2.显示性的数据注解

          我们都注意到,变量FirstName在页面上的实际显示是:First Name。这样做才是符合实际需要的,要想这样做,我们就必须利用DisplayName:

    [Required(ErrorMessage = "First Name is required")]
    [DisplayName("First Name")]
    [StringLength(160, MinimumLength = 3)]
    public string FirstName { get; set; }

         同样是为属性定义说明,还有另一种注解:Dispaly,它的作用并不仅仅是指定显示的内容,甚至能够指定显示的顺序,像是这样:

    [Required(ErrorMessage = "First Name is required")]
    [Display(Name = "First Name", order = 15000)]
    [StringLength(160)]
    public string FirstName { get; set; }

           order的默认参数是10000,如果其他变量并没有设置order值,FirstName就排在其他变量后面。但实际上是不用这么麻烦的,如果不指定该值,就会按照变量声明的顺序排列。

          Display在用于属性的显示名称上,具有更高的优先级。

          DisplayName专门用于定义属性的显示名称,但是Display并不仅仅是如此。它的内容非常丰富,有些在这里不好讲,而且本人是MVC新手,所以还请感兴趣的同学自己查一下相关资料。

          如果对数据库有所了解的话,就会知道,数据库需要key值用于搜索相应的元组。MVC经常与数据库打交道,所以我们的模型中经常需要定义一个或几个名称中包含有Id的作为key的属性,像是OrderId之类的,但是我们在显示的时候又不想将这些属性显示出来,但正如我上面所讲的,默认是会将该模型的所有属性都显示出来。那该怎么办呢?就是对显示隐藏,我们可以使用HiddenInput。

          像是这样:

    public class Person{
        [HiddenInput]
        public string Name { get; set; }
    
        [HiddenInput(DisplayValue = false)]
        public string Sex { get; set; }
    
        public int Age { get; set; }
    }

          然后是控制器:

    public ActionResult Index(){
         Person person = new Person() { Name = "Jos", Sex = "man", Age = 18 };
         return View(person);
    }

          实际的效果如图:
          

           我们可以看到,"Sex"完全被隐藏起来了!

           使用了HiddenInput,默认情况下属性会以只读形式显示出来,像是上面的Name,要想完全隐藏,就得将DisplayValue设置为false。

           我们可以来看看使用了HiddenInput的HTML:

    <div class = "editor-label"><label for = "Name">Name</label></div>
    <div class = "editor-label">Jos<input id = "Name" name = "Name" type = "hidden" value = "Jos"/></div>
    
    <input id = "Sex" name = "Sex" type = "hidden" value = "man"/>
    
    <div class = "editor-label"><label for = "Age">Age</label></div>
    <div class = "editor-field"?
        <input class = "text-box single-line" id = "Age" name = "Age" type = "text" value = "18"/>
    <div>

          这些只要了解就好,MVC会自动帮我们处理。
          我们有时候会想,这样做是好的吗?毕竟,MVC会帮我们自动生成HTML文件,包括一些本该是由程序员自己设置的标记。但必须承认,这样让程序员从千篇一律的编码中解放出来,让我们能够将关注点放在其他更值得关注的方面上,而且,我们程序员依然对生成的HTML文件具有很大的控制权。

          特别强调一下,HiddenInput的命名空间是System.Web.Mvc。

          同样是隐藏属性在HTML上的显示,我们还可以使用ScaffoldColumn特性。

          使用ScaffoldColumn并不是为属性设置type = "hidden",它是直接将该属性从基架中删除。我们知道,MVC可以通过预定义模板来自动生成HTML,这种方式就是基架(Scaffolding)。ScaffoldColumn表示存在于基架中并最终呈现在HTML中的字典。

          像是这样:

    [ScaffoldColumn(false)]
    public int OrderId { get; set; }

         即使从基架中将该属性删除,模型绑定器仍然会试图为该属性赋值,这样就为典型的攻击“重复提交”提供了机会。黑客们可以试图向我们的网页中发送"OrderId = 100"这样的字段,而我们的模型绑定器会为该属性赋值并且有可能赋为黑客提供的值。要想防止这种攻击,我们可以利用Bind特性。
         Bind特性可以选择模型绑定器要绑定的值。像是这样:

    [Bind(Include = "Name, Comment")]
    public class Review{
         public int ReviewId{ get; set;}
         public int ProductId{ get; set;}
         publc string Name{ get; set;}
         public string Comment{ get; set;}
    }

          这样模型绑定器就只绑定Name和Comment属性。
          当然,我们也可以选择不绑定的属性:

    [Bind(Exclude = "OrderId")]
        public partial class Order
        {
            [ScaffoldColumn(false)]
            public int OrderId { get; set; }
            
            [ScaffoldColumn(false)]
            public string UserName { get; set; }
    
         }

         这样,上面所讲的“重复提交”攻击就无法发挥作用了。但是必须注意,使用"Include"的白名单比起使用"Exclude"的黑名单更加安全,因为我们永远也不知道黑客会用怎样的方式来攻击我们,但确定的是,我们可以知道哪些被绑定的属性是不会造成太大的危害的。

          使用白名单还是黑名单还是得看具体情况,毕竟有时候我们需要绑定的属性非常多,如果采用白名单的方式,光是数据注解这里可能就要超过了我们该模型类的代码总量了。

          Bind既可以用于模型,也可以用于控制器操作的参数,但更多时候还是用在模型上。

          提到模型绑定器,这里就必须得说一下属性的更新问题。模型绑定器会更新被绑定的属性的值,但有些值我们可能希望它永远都不要被更新,那么我们就可以使用ReadOnly特性。

          像是这样:

    [ReadOnly(true)]
    public decimal Total{ get; set;}

          模型绑定器就不会更新它的值,虽然实际运行的时候,我们的确可以在文本框里修改它的值,但是该值并不会被用来更新该属性的值。

          ReadOnly的命名空间是在using System.ComponentModel。

          既然提到可读,那么一定存在可写。是的,在System.ComponentModel.DataAnnotations中就有一个Editable。

          它的使用方式和ReadOnly完全一样,但我们会很好奇:如果同时将这两个注解用在同一个属性上,会怎么样呢?事实上,Editable拥有更高的优先级。
          接下来讲到的数据注解就真的非常重要。所有显示性的数据注解,其实都是提供给HTML 辅助方法和ASP.NET MVC运行时的其他组件使用,这些都是靠模型元数据提供器来负责收集。所以,就有一种数据注解能够为运行时提供关于属性的特定用途。这就是DataType。

          我们会在用户的登陆界面中要求用户输入登陆密码,但是又必须保证,这些密码不会显示出来以免被别人看到,这时,DataType就发挥作用了:

    [Required]
    [DataType(DataType.Password)]
    [DisplayName("Password")]
    public string Password { get; set; }

          从上面可以看出,DataType所谓的数据类型,并不是我们常说的int,float等,事实上,DataType本身也有一个可支持的数据类型枚举:

    public enum DataType{
        Custom, DateTime, Date, Time, Duration, 
    PhoneNumber, Currency, Text, Html, MultilineText,
    EmailAddress, Password, Url, ImageUrl, CreditCard,
    PostalCode, Upload }

         DataType实际上是一个验证特性,它继承自ValidationAttribute,但我之所以放在这里讲,是因为所谓的"显示"性,是指它的作用效果,像DataType的作用效果就是为它内置的数据类型提供不同的显示效果。
         当然,我们还可以自定义自己的数据类型,不过这种情况下我们就必须自定义该数据类型的DisplayFormat。这些都在DataType的源码中:

    [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Method, AllowMultiple=false)]  public class DataTypeAttribute : ValidationAttribute{
        public DataTypeAttribute(DataType dataType); 
        public DataTypeAttribute(string customDataType);
        public virtual string GetDataTypeName();
        public override bool IsValid(object value);
        public string CustomDataType { get; }
        public DataType DataType { get;  }
        public DisplayFormatAttribute DisplayFormat { get; }
    }

          我们有时候想要对输出进行格式化设置,这时我们就可以利用DisplayFormat。

          像是这样:

    [Required(ErrorMessage = "Price is required")]
    [DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:c}")]
    [Range(0.01, 100.0, ErrorMessage = "Price must be between 0.01 and 100.0")]
    public decimal Price { get; set; }

          就会有这样的效果:    

     

          话说,中文版的VS2012莫非真的是入乡随俗,我看到外国的教程都是美元标记,但这里却是RMB标记!但这点确实真的很重要,毕竟,在中国发布的网站价格标注的要是美元的话,那还真的是没有多少人会买了。

          这里必须注意,ApplyFormatInEditMode的默认值是false,之所以这样,是因为我们模型绑定器无法解析格式化的值。

          查看DisplayFormat的源码,我们就可以发现,它还有两个个布尔类型的属性:HtmlEncode和ConvertEmptyStringToNull,前者表示是否需要对目标内容进行HTML编码,默认下是true(这样是为了安全性的考虑),后者表示是否将传入的空字符串转换成Null。

          DisplayFormat还有一个string属性:NullDisplayText,它表示针对空值(Null)对象的显示文本。

          同样的道理,因为DataType也对应着一个DisplayFormatAttribute,所以当这两个特性同时应用在相同的元素上,后者具有更高级的优先级。

          最后一个要讲的数据注解,因为不知道要放在哪里,但提到模板的话,就还是放在这里好了。它就是UIHintAttribute。

          我们知道,HTML辅助方法会帮我们选择基于Model的模板方法。所谓的模板方法,指的是我们在通过调用这些方法来将Model的数据显示在View中时,采用默认或者指定的模板来决定最终呈现在浏览器中的HTML。这点在我们使用MVC的时候就已经察觉了:模板名称对应具体的Model元数据。所以,通过元数据,我们可以自定义要选择的模板。像是这样:

    public class Person{
        [UIHint("Template A")]
        public string Name{ get; set;}
    
        [UIHint("Template B", "Mvc")]
        [UIHint("Template A")]
    public string Sex{ get; set;} }

          它有两个构造函数:

    public UIHintAttribute(string uiHint); 
    public UIHintAttribute(string uiHint, string presentationLayer);

          对于最后一个属性Sex,最终采用的是Template B,因为它的presentationLayer的值为Mvc。

          本人完全是MVC新手,所讲的均是自己的理解,如果有说得不对的地方,还请各位大神指出。

         

        

         

  • 相关阅读:
    find命令进阶(二):对找到的文件执行操作exec
    find命令进阶用法(一)
    find按照文件大小查找
    find命令查找目录
    什么是ppa
    Linux进程管理命令
    [HDOJ4135]Co-prime
    [HDOJ5391]Zball in Tina Town
    [模拟]位运算实现四则运算
    [HDOJ1233]还是畅通工程
  • 原文地址:https://www.cnblogs.com/wenjiang/p/2998793.html
Copyright © 2020-2023  润新知