===========一、基础=============
对于web开发人员来说,对用户输入的信息进行验证是一个重要但是繁琐的工作,而且很多开发者都会忽略。asp.net mvc3框架使用的是叫做“数据注解”(
DataAnnotations)的方式进行数据验证。
这种方式允许程序在客户端和服务器端进行双重验证(asp.net的数据验证控件也是双重验证的)。双重验证的好处是 客户端验证会直接相应用户,不用提交表单,也就减轻了服务器的压力还提高了用户体验;而服务器端验证是确保数据的有效性和完整性,因为有时候客户端会关闭脚本功能。
注意:要实现客户端验证,需要引用jquery以及jquery验证文件:
<script src="@Url.Content("~/Scripts/jquery-1.5.1.min.js")" type="text/javascript"></script> <script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script> <script src="@Url.Conten("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
要实现服务器端验证,需要在提交表单后执行ModelState.IsValid
一、基础验证
1、Require(非空验证)
Model:
[Required] [Display(Name = "用户名")] public string UserName { get; set; }
验证结果:
2、StringLength(字符串长度验证)
可以验证model属性的最大长度和最小长度,分别对应
MaximumLength和
MinimumLength,其中MinimumLength是可选的。
Model:
[Required] [StringLength(100, MinimumLength = 6)] [DataType(DataType.Password)] [Display(Name = "密码")] public string Password { get; set; }
验证结果
3、RegularExpression(正则表达式)
验证符合该正则表达式的属性。
Model:
[Required] [DataType(DataType.EmailAddress)] [RegularExpression(@"[A-Za-z0-9._%+-]+@[A-Za-z0-9._]+\.[A-Za-z]{2,4}")] [Display(Name = "电子邮件地址")] public string Email { get; set; }
验证结果:
4、Range(数值范围验证)
用来指定数值的最大值和最小值,第一个参数是最小值,第二个参数是最大值(包含他们本身)。可以用户int类型以及Double类型。
Model:
[Required] [Range(13, 18)] [Display(Name="年龄")] public int Age { get; set; }
验证结果:
二、附加验证
附加验证是System.Web.Mvc中额外添加的两个验证特性,所以使用时必须添加using System.Web.Mvc
1、Remote(远程验证)
虽然asp.net mvc3提供了很多在model中直接验证数据的特性,但是很多逻辑复杂的验证没办法在model中来验证,所以mvc3框架提供了这个远程验证特性,他允许开发者在Controller中用C#代码来验证数据的有效性。
一个很经典的应用时验证用户名是否重复。这个数据验证没办法在客户端验证,除非将所有的用户名都发往客户端(显然这是不可能的)。所以Remote特性只进行服务器端验证。但是它是通过异步的方式进行验证,所以有更好的用户体验。
Model:
[Required] [Display(Name = "用户名")] [Remote("CheckUserName","Account")] public string UserName { get; set; }
Controller:
public JsonResult CheckUserName(string userName) { var result = userName == "admin"; return Json(result, JsonRequestBehavior.AllowGet); }
验证结果:

注意:如果要Post提交,则需要加上HttpMethod=“POST”:
[Required(ErrorMessageResourceType=typeof(ErrorMessage),ErrorMessageResourceName="UserRequire")] [Display(Name = "用户名")] [Remote("CheckUserName","Account", HttpMethod="POST")] public string UserName { get; set; }
而Controller中也可以直接接受Post的请求,当然你加上[Httppost]也是可以的:
//[HttpPost] public JsonResult CheckUserName(string userName) { var result = userName != "admin"; return Json(result, JsonRequestBehavior.AllowGet); }
2、Compare(相同验证)
Compare特性是用来验证输入的两个数据是否完全相同。最典型的例子就是注册时两次输入的密码是否相同。
Model:
[Required] [StringLength(100, ErrorMessage = "{0} 必须至少包含 {2} 个字符。", MinimumLength = 6)] [DataType(DataType.Password)] [Display(Name = "密码")] public string Password { get; set; } [DataType(DataType.Password)] [Display(Name = "确认密码")] [Compare("Password", ErrorMessage = "密码和确认密码不匹配。")] public string ConfirmPassword { get; set; }
验证结果:
上面的是asp.net mvc3框架的几种常用的数据注解,除了后两种mvc特有的特性,其他的特性都在System.ComponentModel.DataAnnotations中,恰当的使用会大大提高开发效率。具体的参见:
http://msdn.microsoft.com/zh-cn/library/system.componentmodel.dataannotations.aspx
===========二、错误信息的自定义及其本地化=============
一、自定义错误信息
在上一篇文章中所做的验证,在界面上提示的信息都是系统自带的,有些读起来比较生硬。比如:
;
如果你的环境是英文的,那么你的提示信息就是中英文夹杂的了。
在这种情况下就需要用到自定义错误信息了。其实很简单,就是在model的验证特性中加上[ErrorMessage]。比如:
Model:
[Required(ErrorMessage = "用户名不能为空!")] [Display(Name = "用户名")] [Remote("CheckUserName","Account")] public string UserName { get; set; }
验证结果:
ErrorMessage允许开发者使用{0}占位符来显示字段的显示名(即
[Display(Name = "用户名")]),如果没有Display特性,那么会显示属性名。如:
Model:
[Required(ErrorMessage = "{0}不能为空!")] [Display(Name = "用户名")] [Remote("CheckUserName","Account")] public string UserName { get; set; }
验证结果:
若为:
[Required(ErrorMessage = "{0}不能为空!")] //[Display(Name = "用户名")] [Remote("CheckUserName","Account")] public string UserName { get; set; }
则验证结果为:
如果验证的特性中还有其他参数,那么ErrorMessage可以用占位符直接显示其他参数,如
Model:
[Required] [StringLength(100, ErrorMessage = "请输入{2}到{1}位的{0}。", MinimumLength = 6)] [DataType(DataType.Password)] [Display(Name = "密码")] public string Password { get; set; }
验证结果:
二、错误信息的本地化
如果你做的项目是要分发到不同的国家,那么就需要做本地化。而对于错误信息而言,也有这样的功能,方法如下:
1、在项目中添加两个资源文件ErrorMessages.resx以及ErrorMessages.en-US.resx:
2、在两个资源文件中都加入名称为UserNameRequire的资源,值分别为中英文下的提示信息:
3、在web.config的
<system.web>节点加入
<globalization uiCulture="auto"/>,随着浏览器的设置来更换资源文件。
4、在Model中也如如下代码:
[Required(ErrorMessageResourceType = typeof(ErrorMessages), ErrorMessageResourceName = "UserNameRequire")] [Display(Name = "用户名")] public string UserName { get; set; }
5、运行结果:
中文环境下:
英文环境下:
好了,mvc3中错误信息的自定义和本地化比较简单,暂时总结这么多,下一篇就要讲自定义数据验证了。
===========三、自定义数据注解=============
前两节讲的都是asp.net mvc3预先设定的数据注解,但是系统自由的数据注解肯定不适合所有的场合,所以有时候我们需要自定义数据注解。
自定义数据注解有两种,一种是直接写在模型对象中,这样做的好处是验证时只需要关心一种模型对象的验证逻辑,缺点也是显而易见的,那就是不能重用。
还有一种是封装在自定义的数据注解中,优点是可重用,缺点是需要应对不同类型的模型。
现在我们以封装在自定义数据注解中的方法为例看下如何在asp.net mvc3中自定义数据注解以及使用。
一、自定义属性级别的验证
首先,所有的数据注解都应继承于
System.ComponentModel.DataAnnotations命名空间中的
ValidationAttribute类。
重写其
protected virtual ValidationResult IsValid(object value, ValidationContext validationContext);
例如:
我们需要写一个UserName不能超过10个字母的数据注解(你可能会说这不是有的
StringLength么,好吧,仅以此为例,我真没想到其他的需要自定义数据注解的好例子)。
(1)新建一个类
MaxLengthAttribute,代码如下:
public class MyMaxLengthAttribute : ValidationAttribute { private readonly int MaxLength; public MyMaxLengthAttribute(int maxLength) { MaxLength = maxLength; } protected override ValidationResult IsValid(object value, ValidationContext validationContext) { string content = value.ToString(); if (content.Length > MaxLength) { return new ValidationResult("输入的字符太多了!^_^"); } return ValidationResult.Success; //return base.IsValid(value, validationContext); }
第二步就是像正常使用asp.net自带的数据注解一样使用,如:
[Required(ErrorMessageResourceType=typeof(ErrorMessage),ErrorMessageResourceName="UserRequire")]
[Display(Name = "用户名")]
[MyMaxLengthAttribute(10)]
[Remote("CheckUserName","Account", HttpMethod="POST")]
public string UserName { get; set; }
好了,只需要这样简单的两步就可以实现了。
验证结果:
对于自定义的数据注解由于是继承于
System.ComponentModel.DataAnnotations命名空间中的
ValidationAttribute类,所以它的一些属性也可以使用,比如ErrorMessage,如:
[Required(ErrorMessageResourceType=typeof(ErrorMessage),ErrorMessageResourceName="UserRequire")]
[Display(Name = "用户名")]
[MyMaxLengthAttribute(10,ErrorMessage="{0}字数太多")]
[Remote("CheckUserName","Account", HttpMethod="POST")]
public string UserName { get; set; }
需要注意的是,自定义的数据注解不支持客户端验证,所有的数据需要提交之后再服务端验证,所以如果要同时实现客户端验证需要自己写js验证。
但是这样的验证有一个问题,就是默认的验证信息不能实现直接显示Display Name,所以需要如下更改:
public class MyMaxLengthAttribute : ValidationAttribute { private readonly int MaxLength; public MyMaxLengthAttribute(int maxLength ):base("{0}的字符太多了!") { MaxLength = maxLength; } protected override ValidationResult IsValid(object value, ValidationContext validationContext) { string content = value.ToString(); if (content.Length > MaxLength) { //return new ValidationResult("输入的字符太多了!^_^"); string errorMessage = FormatErrorMessage(validationContext.DisplayName); return new ValidationResult(errorMessage); } return ValidationResult.Success; //return base.IsValid(value, validationContext); } }
验证结果:
二、自定义Model级别的验证(IValidatableObject)
这个接口是为了实现Model的自验证(self-validating)的,是asp.net mvc3 新增的验证特性。这个特性和普通数据注解的区别是普通数据注解仅仅只能验证Model的一个属性,而实现了IValidatableObject接口的自验证则在Model的级别来验证,比如验证Model的几个属性之间的关系等。
例如,我要验证两次输入的密码相同(好吧,我又把系统自带的验证再写一遍)。
(1)首先,要将需要验证的Model实现IValidatableObject接口。
public class RegisterModel : IValidatableObject
(2)在Model中实现
Validate方法:
public IEnumerable<ValidationResult> Validate(ValidationContext validationContent) { if (Password != ConfirmPassword) { yield return new ValidationResult("两次输入的密码不同!", new[] { "Password" }); } }
这个方法在提交Model时会自动验证两次输入的密码是否相同,如果不同则会提示,如下:
注意:1、自验证只能把方法写在需要验证的Model中,所以这种自验证的代码无法重用;
2、自验证的返回值是
IEnumerable<ValidationResult>,而不是
ValidationResult,所以返回值可以不止一个验证错误。
3、Validate方法没有传入value参数,也就是意味着Validate方法可以直接访问Model中的属性值。
4、返回值使用的是yield return来构建枚举返回值,第二个参数是指定错误信息绑定的属性,因为是string数组,所以可以关联多个属性。
======================== 四、Remote实现多重验证==========================
一般来说对于一个属性的验证可能需要不止一个的远程验证,比如对于用户名来说,我们需要对于它的长度做一些限制,这个可以通过StringLength特性来解决;同时还需要验证用户名不能重复,这个就需要通过Remote特性来解决。
现在需要再添加一个验证,那就是用户名不能包含违禁词。比如......(比较敏感,就不写了。。。^_^),违禁词保存在数据库中。刚开始我觉得这个也简单,再加个Remote验证不就解决了么。。。童鞋们,有时候我们觉得很简单就能办到的事情,如果不做,永远不知道你能不能办到。所以建议大家,遇到事情不要拖拉,尽快做好。
噼里啪啦,很快就把验证的方法写好了,然后加到Model中一看傻眼了。。。

原来不支持同一个属性有两个Remote验证。。。
这个时候该怎么办呢,尝试将两个验证写在同一个Remote中,但是这样的话只能显示一个ErrorMessage,被否决。(但是的我以为Remote特性的方法只能返回一个bool值,不能返回ErrorMessage)。原来验证为false时不用返回bool值,可以直接返回ErrorMessage。方法如下:
public JsonResult CheckUserName(string userName) { if (IsUniqueName(userName) && IsForbiddenName(userName)) { return Json(true, JsonRequestBehavior.AllowGet); } else if (!IsUniqueName(userName)) { return Json("用户名不唯一!", JsonRequestBehavior.AllowGet); } else { return Json("用户名不包含违禁词!", JsonRequestBehavior.AllowGet); } }
这样就可以完美解决需要多个Remote验证的问题了。