一切福田,不離方寸,從心而覓,感無不通。

第6章 数据注解 System.ComponentModel.DataAnnotations 、注解后台原理、自定义验证ValidationAttribute、IValidatableObject

客户端验证对用户向表单中输入的数据给出一个即时反馈。
服务器验证,主要是因为来自网络的信息都是不能信任的。

一、 为验证注解订单

1 、验证注解的使用,自定义错误提示消息

数据注解特性定义在 System.ComponentModel.DataAnnotations 中(但有一个特性不在这个命名空间),它提供了服务器端验证,当模型属性上使用这些特性时,框架也支持客户端验证。在命名空间DataAnnotations中,有4个特性可以用来应对一般验证场合。

(1)、字符非空,最大长度,值范围,两个属性相同,正则表达式

  1.         //字段非空,最大长度160
  2.         [Required(ErrorMessage="FirstName不能为空")]
  3.         [StringLength(160, ErrorMessage = "FirstName太长了")]
  4.         public string FirstName { get; set; }
  1.         //正则表达式验证必须是电子邮件
  2.         [RegularExpression(@"[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}",
  3.             ErrorMessage="邮件地址不正确")]
  4.         public string Email { get; set; }
  1. //字段非空,数值类型最小值,最大值
  2. [Required]
  3. [Range(typeof(decimal), "0.00", "49.99")]
  4. public decimal Total { get; set; }
  1. //对象两个属性拥有相同的值
  2. [Compare("Email")]
  3. public string EmailConfirm { get; set; }

(2)、命名空间 System.Web.Mvc 中 Remote验证,可以利用服务器的回调函数执行客户端的验证逻辑。

  1. //Remote特性把Username的值发送到服务器,然后在服务器端的数据库中与相应的表字段值进行比较
  2. [Remote("CheckUserName","Account")]
  1. //返回一个封装在JSON对象中的布尔类型值(true或false)
  2. public JsonResult CheckUserName(string username)
  3. {
  4. var result = Membership.FindUsersByName(username).Count == 0;
  5. return Json(result, JsonRequestBehavior.AllowGet);
  6. }

2 、本地化

如果应用程序是向国际市场开发的,那么就要为不同的地区显示不同的文本内容。
幸好,所有验证特性都允许为本地化的错误提示消息指定 资源类型名称 和 资源名称。

  1. [Required(ErrorMessageResourceType=typeof(ErrorMessage),
  2. ErrorMessageResourceName="LastNameRequired")]
  3. [StringLength(160,ErrorMessageResourceType=typeof(ErrorMessage),
  4. ErrorMessageResourceName="LastNameTooLong")]
  5. public string LastName { get; set; }

ErrorMessage资源文件中包含的条目(如:LastNameRequired和LastNameTooLong)。
在ASP.NET中,要使用本地化的资源文件,需要将当前线程的UICulture属性设置为相应的地域语言。

3 、注解的后台原理

(1)、验证和模型绑定
验证是什么时候发生的?如何才能知道验证失败?
默认情况下,ASP.NET MVC框架在模型绑定时执行验证逻辑。
A、隐式地执行模型绑定
[HttpPost]
public ActionResult Create(Album album)
{}
B、显示地执行模型绑定:利用控制器的UpdateModel或TryUpdateModel方法显式地执行模型绑定
[HttpPost]
public ActionResult Edit(int id,FormCollection collection)
{
var album=storeDB.Albums.Find(id);
  if(TryUpdateModel(album))
{}
}
模型绑定器一旦使用新值完成对模型属性的更新,就会利用当前的模型元数据获取模型的所有验证器。
MVC运行时提供了一个验证器(DataAnnotationModelValidator)来与数据注释一同工作。这个模型验证器会找到所有的验证特性并执行它们包含的验证逻辑。模型绑定器捕获所有失败的验证规则并把它们放入模型状态中。

(2)验证和模型状态
模型状态(利用Controller派生类对象的ModelState属性可以访问到)。
模型状态不仅包含了用户想放入模型属性中的所有值,也包括与每个属性相关联的所有错误。
如果在模型状态中存在错误,ModelState.IsValid就返回false
例如:顾客没填写LastName值的情况下,提交表单。由于设置了Required验证注解特性,因此在模型绑定之后:

  1. ModelState.IsValid==false
  2. ModelState.IsValidField("LastName")==false
  3. ModelState["LastName"].Errors.Count>0

可在模型状态中查看与失败验证相关的错误提示消息:

例如:ValiadationMessage辅助方法可以通过查看模型状态来显示与特定部分视图数据相关的错误提示消息:

4 、控制器操作和验证错误

控制器操作决定了在模型验证失败或验证成功时的执行流程。
验证成功时,操作通常会执行必要的步骤保存或更新客户信息。
验证失败时,操作一般会重新渲染提交模型的视图,这样就可以让用户看到所有的验证错误提示信息。

二、 自定义验证逻辑

1 、自定义注解

示例:姓氏中单词的数量不得超过10个,并且要让这种验证在其他模型中重用。

  1. public class MaxWordsAttribute:ValidationAttribute
  2. {
  3. //向构造函数中传递一个默认的错误提示消息,包含一个参数占位符{0}
  4. public MaxWordsAttribute(int maxWords):base("{0}字符太多")
  5. {
  6. _maxWords = maxWords;
  7. }
  8. //第一个参数是验证对象的值
  9. protected override ValidationResult IsValid(object value, ValidationContext validationContext)
  10. {
  11. if (value != null)
  12. {
  13. var valueAsString = value.ToString();
  14. if(valueAsString.Split(' ').Length>_maxWords)
  15. {
  16. //FormatErrorMessage方法会自动使用显示的属性名称来格式化这个字符串
  17. var errorMessage = FormatErrorMessage(validationContext.DisplayName);
  18. return new ValidationResult(errorMessage);
  19. }
  20. }
  21. return ValidationResult.Success;
  22. }
  23. private readonly int _maxWords;
  24. }

使用方法:

  1. [Required]
  2. [MaxWords(10)]
  3. public string LastName {get;set;}

甚至可以赋予特性自定义的错误提示消息:

  1. [Required]
  2. [MaxWords(10,ErrorMessage="字符长度最大为{0}")]
  3. public string LastName {get;set;}

 

2、 IValidatableObject

模型对象通过实现IValidatableObject接口来实现对自身的验证。

  1. public class Order:IValidatableObject
  2. {
  3. public int OrderId { get; set; }
  4. public string LastName { get; set; }
  5. public string Address { get; set; }
  6. public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
  7. {
  8. if(LastName!=null && LastName.Split(' ').Length>10)
  9. {
  10. yield return new ValidationResult("LastName太长!",new []{"LastName"});
  11. }
  12. }
  13. }

三、 显示和编辑注解

1、 Display

  1. [Required]
  2. [Display(Name="姓名")]
  3. public string LastName { get; set; }

2、 ScaffoldColumn

3、 DisplayFormat

显示的格式化

4 、ReadOnly

5 、DataType

6 、UIHint

7 、HiddenInput

四、 小结

 

from:https://blog.csdn.net/litao2/article/details/78568364