现在看asp.net MVC5自学已经到了第六章:数据注解与验证。
话得从以前看MVC music store(音乐商店项目)的源码说起,
最初看music store源码完全就是一脸懵逼,整个程序,找了半天,只看到控制器有少许逻辑代码,例如编辑专辑的视图里面,用户输入的title到底符不符合规范, VIEW里面即无相关验证的JS代码,又没有进行后台的数据判断。
1 @using (Html.BeginForm()) { 2 @Html.ValidationSummary(true) 3 <fieldset> 4 <legend>Album</legend> 5 6 @Html.HiddenFor(model => model.AlbumId) 7 8 <div class="editor-label"> 9 @Html.LabelFor(model => model.GenreId, "Genre") 10 </div> 11 <div class="editor-field"> 12 @Html.DropDownList("GenreId", String.Empty) 13 @Html.ValidationMessageFor(model => model.GenreId) 14 </div> 15 16 <div class="editor-label"> 17 @Html.LabelFor(model => model.ArtistId, "Artist") 18 </div> 19 <div class="editor-field"> 20 @Html.DropDownList("ArtistId", String.Empty) 21 @Html.ValidationMessageFor(model => model.ArtistId) 22 </div> 23 24 <div class="editor-label"> 25 @Html.LabelFor(model => model.Title) 26 </div> 27 <div class="editor-field"> 28 @Html.EditorFor(model => model.Title) 29 @Html.ValidationMessageFor(model => model.Title) 30 </div> 31 32 <div class="editor-label"> 33 @Html.LabelFor(model => model.Price) 34 </div> 35 <div class="editor-field"> 36 @Html.EditorFor(model => model.Price) 37 @Html.ValidationMessageFor(model => model.Price) 38 </div> 39 40 <div class="editor-label"> 41 @Html.LabelFor(model => model.AlbumArtUrl) 42 </div> 43 <div class="editor-field"> 44 @Html.EditorFor(model => model.AlbumArtUrl) 45 @Html.ValidationMessageFor(model => model.AlbumArtUrl) 46 </div> 47 48 <p> 49 <input type="submit" value="Save" /> 50 </p> 51 </fieldset> 52 }
音乐商店编辑专辑的视图
程序到底是怎么完成数据验证呢?
今天看了这一章对数据验证就有了大概的了解了。
===========================================================
前提:C# Attribute特性和反射
翻到专辑的Model(实体类)里,我们看到Album类的定义是这样的。
public class Album { [ScaffoldColumn(false)] public int AlbumId { get; set; } [DisplayName("Genre")] public int GenreId { get; set; } [DisplayName("Artist")] public int ArtistId { get; set; } [Required(ErrorMessage = "An Album Title is required")] [StringLength(160)] public string Title { get; set; } [Required(ErrorMessage = "Price is required")] [Range(0.01, 100.00, ErrorMessage = "Price must be between 0.01 and 100.00")] public decimal Price { get; set; } [DisplayName("Album Art URL")] [StringLength(1024)] public string AlbumArtUrl { get; set; } public virtual Genre Genre { get; set; } public virtual Artist Artist { get; set; } public virtual List<OrderDetail> OrderDetails { get; set; }
原来,模型类里面大量采用attribute特性
关于Attribute特性我原先一直没有搞懂它到底是怎么运作的。写个方括号,括起来一个类,为什么就能让我的属性、方法、类有了限制?
下面我首先简介一下Attribute特性的一些基本要点:
1、特性类其实就是一个从Attribute基类继承而来的类。
2、自定义Attribute只能在反射时生效。
等等,第二个基本要点中:反射。它的定义就是反射指程序可以访问、检测和修改它本身状态或行为的一种能力。
这也就是说,利用反射,我们可以得到一个类里面所有的特性。似乎我可以猜测到一点特性的运行原理了:我们可以在特性类中做文章,在反射时,获取了被标注的类的所有特性,运行这个这些特性的某些方法或者属性,就可以对这个被标注的类进行验证或者限制。
验证的运行原理:利用特性来进行验证。
[StringLength(160)] public string Title { get; set; }
在上面一个属性中,有一个StringLengt(160)的特性,MVC书上称他为验证。
// 摘要: // 指定数据字段中允许的最小和最大字符长度。 [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false)] public class StringLengthAttribute : ValidationAttribute { // 摘要: // 使用指定的最大长度初始化 System.ComponentModel.DataAnnotations.StringLengthAttribute // 类的新实例。 // // 参数: // maximumLength: // 字符串的最大长度。 public StringLengthAttribute(int maximumLength); // 摘要: // 获取或设置字符串的最大长度。 // // 返回结果: // 字符串的最大长度。 public int MaximumLength { get; } // // 摘要: // 获取或设置字符串的最小长度。 // // 返回结果: // 字符串的最小长度。 public int MinimumLength { get; set; } // 摘要: // 对指定的错误消息应用格式设置。 // // 参数: // name: // 要进行格式设置的错误消息。 // // 返回结果: // 带有格式的错误消息。 // // 异常: // System.ArgumentOutOfRangeException: // maximumLength 为负数。- 或 -maximumLength 小于 minimumLength。 public override string FormatErrorMessage(string name); // // 摘要: // 确定指定的对象是否有效。 // // 参数: // value: // 要验证的对象。 // // 返回结果: // 如果指定的对象有效,则为 true;否则为 false。 // // 异常: // System.ArgumentOutOfRangeException: // maximumLength 为负数。- 或 -maximumLength 小于 System.ComponentModel.DataAnnotations.StringLengthAttribute.MinimumLength。 public override bool IsValid(object value); }
翻开它的元数据我们可以看到,它其实就是一个微软提供的自定义特性。它继承自ValidationAttribute,ValidationAttribute又继承自Attribute
通过元数据我们可以看到,它其中有一个重写的bool IsVlid(object value) 方法,它就是用来判断被标注的对象是否验证成功的入口。
实际上,翻开ValidationAttribute的元数据,IsValid方法提供了很多重载版,验证特性要成功,最少都要重写一个方法,在方法里进行逻辑判断,确定是否验证成功。
==========================================================================================
MVC特性验证的原理
以下是书上的解释:
1、asp.net mvc框架可以自动绑定模型,如果控制器带了参数,MVC运行时会隐式地根据参数类型的标识符,从GET或者POST来的键值对里面找相同名字的键填充值。当然,控制器不带参数,也可以通过tryupdatemodel,或者updatemodel两个方法来完成模型绑定。
2、在模型绑定之后,下一步就是反射被绑定后的模型,反射获取这个模型对象的验证特性,也就是例如[StringLength(160)]这些特性。
3、得到所有验证特性之后,就调用这些验证特性类的isvalid方法,对被绑定属性进行验证。
4、MVC运行时会捕捉所有isvalid方法返回值为false的验证规则,并把他们加入到ModelState中。
5、如果ModelState中有任何一个验证的失败信息,ModelState的isValid属性都将设定为False。
6、在前端视图中,我们就可以调用一个辅助方法: @Html.ValidationMessageFor(model => model.GenreId),去ModelState中找这个错误,辅助方法会自动根据ModelState中的错误信息拼装包含你传入的数据抛出的验证错误的HTML。
如此一来,通过这6个步骤,仅仅依靠特性就能对数据进行验证。大大地提高了编码效率,减少了编码量