MVC之验证终结者篇

来源:互联网 发布:广州java培训机构 编辑:程序博客网 时间:2024/04/30 06:02

一、基于数据注解特性的验证

SO EASY:

A、你如果想用数据注解特性,那就必须要引入以下命名空间:using System.ComponentModel.DataAnnotations;

可以注意到,这个命名空间不是以System.Web开头的,这也说明了它并不单单是为Web项目准备的,如何其它类型的项目都可以使用。

例如:对于一个注册用的model,在其上面用特性验证

using System.ComponentModel.DataAnnotations;namespace Ctrip.Models{  public class Register  {    [Required(ErrorMessage="用户名必须填写")]    [MinLength(6,ErrorMessage="用户名长度过短")]    public String UserName { get; set; }    [DataType(DataType.Password)]    public String Password { get; set; }    [DataType(DataType.Password)]    [Compare("Password",ErrorMessage="密码要一致")]    public String RptPassword { get; set; }    public String Email { get; set; }    public DateTime BirthDate { get; set; }  }}

注:错误消息还可以用占位符,体会一下好处在哪

[Required(ErrorMessage=”Your {0} is required.”)]public string Name { get; set; }

B、在view中用HtmlHelper的方法就可以实现客户端验证了(前提是要开启客户端验证)

@Html.ValidationSummary()会输出所有验证错误, 一般放在Form头处 @Html.ValidationMessageFor()是针对单个输入的验证

大概就是下面的格式:

@using (Html.BeginForm()) {   @Html.AntiForgeryToken()   @Html.ValidationSummary()  <fieldset>     <legend>Registration Form</legend>     <ol>       <li>         @Html.LabelFor(m => m.UserName)         @Html.EditorFor(m => m.UserName)         @Html.ValidationMessageFor(m => m.UserName)       </li>       <li>         @Html.LabelFor(m => m.Password)         @Html.EditorFor(m => m.Password)         @Html.ValidationMessageFor(m => m.Password)       </li>        <li>         @Html.LabelFor(m => m.Email)         @Html.TextBoxFor(m => m.Email)         @Html.ValidationMessageFor(m => m.Email)       </li>       <li>         @Html.LabelFor(m => m.BirthDate)         @Html.EditorFor(m => m.BirthDate)         @Html.ValidationMessageFor(m => m.BirthDate)       </li>       </ol>     <input type="submit" value="Register" />   </fieldset> }

这个时候如果你不处理后端,只要客户端允许JS运行,就可以在客户端验证,而且是AJAX验证哦,不解释~

备注:客户端验证在哪里开启与关闭呢?在web.config里边,true or false,你说了算!

<appSettings>    <add key="ClientValidationEnabled" value="false" />    <add key="UnobtrusiveJavaScriptEnabled" value="true" /> </appSettings>

有些人说在控制器里边写一个专门的Valid()方法验证,我说这不是作死的节奏啊,有肉还吃豆腐?呵呵,说的就是下面这个东东ModelState.IsValid

[HttpPost]       public ActionResult Create(Register register)       {           if (ModelState.IsValid)           {    return RedirectToAction("Index", "Home");           }           return View(register);       } 

D、验证特性特别多,想要看的自己上网查,或者反编译上面的那个命名空间就可以看到!不过有两个特性比较特殊,是在System.Web.Mvc命名空间里边:      RemoteAttribute和CompareAttribute,CompareAttribute的用法上面的A中已有,下面举个RemoteAttribute的例子,就是我们在网站注册的时候,要求用户名不能重复,可以用这个RemoteAttribute实现,是异步的~

public class Employee{  public int EmpId { get; set; }  [DisplayName("Employee Name")]  [Remote("IsEmployeeNameAvailable", "Validation")] //使用RemoteAttribute,指定验证的Controller和Action  public String EmployeeName { get; set; }}

然后你再写个Action就行了,由于比较简单这里就不再写了!

警告:这个RemoteAttribute是只进行客户端验证的,服务器端不验证的,所以如果某个用户屏蔽了客户端js那就没用了

注:正则表达式特性也很有用的,例如:

[RegularExpression(@”[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}”,ErrorMessage=”Email doesn’t look like a valid email address.”)]

具体的正则表达式你可以上网搜~

二、自定义数据注解特性验证

先说说什么是双重验证?

其实就是客户端验证加服务器端验证而已~

为什么要双重验证?

首先,客户端验证能够直接响应客户,减少了服务器压力的同时还提高了用户体验,但是你永远不能信任来自客户端的信息(用户可以关闭浏览器的脚本功能,让你的js验证彻底不起作用),所以服务器端验证也是必须的~

常规验证可以通过上面列出的这些系统预定义ValidationAttribute特性来完成,但是在很多情况下我们需要通过创建自定义的ValidationAttribute特性来解决一些特殊的验证,而且这些自定义的特性在很多地方可以重用的!

你需要做的就是重写ValidationAttribute里边的方法IsValid()而已

举个栗子:

using System.ComponentModel.DataAnnotations;using System.Text.RegularExpressions;namespace MvcValidation.Extension{  public class EmailAttribute : ValidationAttribute  {    public const string reg = @"^[\w-]+(\.[\w-]+)*@([\w-]+\.)+[a-zA-Z]+$";    public EmailAttribute()    {      }    //重写基类方法    public override bool IsValid(object value)    {      if (value == null)        return true;      if (value is string)      {        Regex regEx = new Regex(reg);        return regEx.IsMatch(value.ToString());      }      return false;    }  }}

用的话,就是下面的样子:

[Email] public string Email { get; set; }

注意:

此时的自定义的验证特性只支持后端验证,如果想支持前端jquery验证,还需要实现 IClientValidatable接口

所以改写一下:

using System.ComponentModel.DataAnnotations;using System.Text.RegularExpressions;using System.Web.Mvc;namespace MvcValidation.Extension{  public sealed class EmailAttribute : ValidationAttribute, IClientValidatable  {    public const string reg = @"^[\w-]+(\.[\w-]+)*@([\w-]+\.)+[a-zA-Z]+$";    public EmailAttribute()    {      }    //重写基类方法    public override bool IsValid(object value)    {      if (value == null)        return true;      if (value is string)      {        Regex regEx = new Regex(reg);        return regEx.IsMatch(value.ToString());      }      return false;    }    public System.Collections.Generic.IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)    {      ModelClientValidationRule rule = new ModelClientValidationRule      {        ValidationType = "email",        ErrorMessage = FormatErrorMessage(metadata.GetDisplayName())      };      yield return rule;    }  }}

注意:

ValidationType属性的值一定要小写,否则报错。

其实还没有结束呢,还要扩展JQuery函数(jQuery.validator.email.js文件)

//扩展方法$.validator.addMethod("email", function (value, element) {  if (value == false) {    return true;  }  this.optional(element) || /^[\w-]+(\.[\w-]+)*@([\w-]+\.)+[a-zA-Z]+$/i.test(value);});//扩展方法注册$.validator.unobtrusive.adapters.addBool("email");

其实实现IClientValidatable之后,就是为前端的Email输入input标签添加了一个data-val-email属性而已,属性中的“email”就是ValidationType = " email " 中的名字。

三、带自我验证的模型  

适用场景:不需要重用验证逻辑,只是针对某一个特定的模型进行验证。

这些带自我验证的模型实现了接口IValidatableObject,该接口定义在“System.ComponentModel.DataAnnotations”命名空间下。

public interface IValidatableObject{    IEnumerable<ValidationResult> Validate(  ValidationContext validationContext);}

举个栗子:

public class Person: IValidatableObject{  [DisplayName("姓名")]  public string Name { get; set; }  [DisplayName("性别")]  public string Gender { get; set; }  [DisplayName("年龄")]  public int? Age { get; set; }  public IEnumerable<ValidationResult> Validate( ValidationContext validationContext)  {    Person person = validationContext.ObjectInstance as Person;    if (null == person)    {      yield break;    }    if(string.IsNullOrEmpty(person.Name))    {      yield return new ValidationResult("'Name'是必需字段", new string[]{"Name"});    }    if (string.IsNullOrEmpty(person.Gender))    {      yield return new ValidationResult("'Gender'是必需字段", new string[] { "Gender" });    }    else if (!new string[]{"M","F"}.Any( g=>string.Compare(person.Gender,g, true) == 0))    {      yield return new ValidationResult("有效'Gender'必须是'M','F'之一",   new string[] { "Gender" });    }    if (null == person.Age)    {      yield return new ValidationResult("'Age'是必需字段",new string[] { "Age" });    }    else if (person.Age > 25 || person.Age < 18)    {      yield return new ValidationResult("'Age'必须在18到25周岁之间",new string[] { "Age" });    }  }}

四、自定义验证的错误消息

通过在自定义验证特性中重写FormatErrorMessage方法来实现:

using System.ComponentModel.DataAnnotations;using System.Text.RegularExpressions;using System.Web.Mvc;namespace MvcValidation.Extension{  public sealed class EmailAttribute : ValidationAttribute, IClientValidatable  {    public const string reg = @"^[\w-]+(\.[\w-]+)*@([\w-]+\.)+[a-zA-Z]+$";    public EmailAttribute()    {      }    //重写基类方法    public override bool IsValid(object value)    {      if (value == null)        return true;      if (value is string)      {        Regex regEx = new Regex(reg);        return regEx.IsMatch(value.ToString());      }      return false;    }    public System.Collections.Generic.IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)    {      ModelClientValidationRule rule = new ModelClientValidationRule      {        ValidationType = "email",        ErrorMessage = FormatErrorMessage(metadata.GetDisplayName())      };      yield return rule;    }/// <summary>/// 格式化错误信息/// </summary>/// <param name="name">属性名</param>/// <returns></returns>    public override string FormatErrorMessage(string name)    {   return  this.ErrorMessage ?? string.Format("{0}属性没有输入正确的Email", name);     }  }}

最后,有一些验证还可以用JQuery实现,基本上属于js的东东,这里不再陈述

3 0