[Required(ErrorMessage="你的名字{0}")]
[RegularExpression(@"正则表达式")]
[Range]
[Compare("别的字段")]
[Remote("CheckUserName", "Validate", ErrorMessage = "遠端驗證失敗")]//服务器验证来返回是否通过
public class ValidateController : Controller { public JsonResult CheckUserName(string userName) { bool isValidate = false; if (Url.IsLocalUrl(Request.Url.AbsoluteUri)) { //利用 IsLocalUrl檢查是否為網站呼叫的 //借此忽略一些不必要的流量 if (userName != "demoshop") { //因連資料庫麻煩 //所以假裝示範不可以註冊某一名字 isValidate = true; } } // Remote 驗證是使用 Get 因此要開放 return Json(isValidate, JsonRequestBehavior.AllowGet); } }
ModelState.IsValid==false
ModelState.IsValidField("LastName")==false
ModelStatel["LastName"].Errors.Count>0
ASP.NET MVC如何实现自定义验证(服务端验证+客户端验证)
一、AgeRangeAttribute
用于验证出生日期字段以确保年龄在制定的范围之内的AgeRangeAttribute定义如下,简单起见,我们直接让它直接继承自RangeAttribute。服务端验证逻辑定义在重写的IsValid方法中,并且重写了FormatErrorMessage方法以便生成针对年龄的验证消息。AgeRangeAttribute实现了IClientValidatable接口,并在实现的GetClientValidationRules方法中生成客户端验证规则。在生成的类型为“agerange”的ModelClientValidationRule 对象中包含三个参数(currentdate、minage和maxage),分别表示当前日期(用于计算年龄)、允许年龄的范围。
1: public class AgeRangeAttribute : RangeAttribute, IClientValidatable2: {
3: public AgeRangeAttribute(int minimum, int maximum)4: : base(minimum, maximum)5: { }
6:
7: public override bool IsValid(object value)8: {
9: DateTime birthDate = (DateTime)value;10: DateTime age = new DateTime(DateTime.Now.Ticks - birthDate.Ticks);11: return age.Year >= (int)this.Minimum && age.Year <= (int)this.Maximum;12: }
13:
14: public override string FormatErrorMessage(string name)15: {
16: return base.FormatErrorMessage("年龄");17: }
18:
19: public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)20: {
21: ModelClientValidationRule validationRule = new ModelClientValidationRule(){ ValidationType = "agerange", ErrorMessage= FormatErrorMessage(metadata.DisplayName)};22: validationRule.ValidationParameters.Add("currentdate",DateTime.Today.ToString("dd-MM-yyyy"));23: validationRule.ValidationParameters.Add("minage",this.Minimum);24: validationRule.ValidationParameters.Add("maxage",this.Maximum);25: yield return validationRule;26: }
27: }
二、注册客户端验证方法
由于ASP.NET MVC采用JQuery Validation进行客户端验证,我们可以通过如下的这段javascript来注册用于实现客户端验证的function和添加相应的adapter。添加到jQuery.validator的用于进行年龄范围验证的function具有三个参数(value、element、params)分别表示被验证的值、元素和传入的参数。验证逻辑必须的三个数值(当前日期、年龄范围最小和最大值)通过参数params获得。而该参数实际上是在添加adapter时从通过上面定义的GetClientValidationRules方法生成的验证规则中获取的。
1: jQuery.validator.addMethod("agerange",2: function (value, element, params) {3:
4: var minAge = params.minage;5: var maxAge = params.maxage;6:
7: var literalCurrentDate = params.currentdate;8: var literalBirthDate = value;9: var literalCurrentDates = literalCurrentDate.split('-');10: var literalBirthDates = literalBirthDate.split('-');11:
12: var birthDate = new Date(literalBirthDates[2], literalBirthDates[1], literalBirthDates[0]);13: var currentDate = new Date(literalCurrentDates[2], literalCurrentDates[1], literalCurrentDates[0]);14: var age = currentDate.getFullYear() - birthDate.getFullYear();15: return age >= minAge && age <= maxAge16: });
17:
18: jQuery.validator.unobtrusive.adapters.add("agerange", ["currentdate", "minage", "maxage"], function (options) {19: options.rules["agerange"] = {20: currentdate: options.params.currentdate,21: minage: options.params.minage,22: maxage: options.params.maxage23: };
24: options.messages["agerange"] = options.message;25: });
三、AgeRangeAttribute的应用
现在我们将AgeRangeAttribute 应用到一个简单的ASP.NET MVC应用中。在通过VS的ASP.NET MVC项目模板创建的空Web应用中,我们定义了如下一个简单的Person类型,我们定义的AgeRangeAttribute 应用到了表示出生日期的BirthDate上,并将允许的年龄上、下限设置为18和30。
1: public class Person2: {
3: [DisplayName("姓名")]4: public string Name { get; set; }5:
6: [AgeRange(18, 30, ErrorMessage = "{0}必须在{1}和{2}之间!")]7: [DisplayName("出生日期")]8: [DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:dd-MM-yyyy}")]9: public DateTime? BirthDate { get; set; }10: }
然后我们添加如下一个HomeController,在默认的Action方法Index中我们将创建的Person对象呈现在默认的View中。
1: public class HomeController : Controller2: {
3: public ActionResult Index()4: {
5: return View(new Person{ BirthDate = DateTime.Today, Name = "Foo"});6: }
7: [HttpPost]
8: public ActionResult Index(Person person)9: {
10: return View(person);11: }
12: }
如下所示的代码片断代表了View的定义,我们直接调用HtmlHelper<TModel>的扩展方法EditorModel将作为Model的Person对象以编辑模式呈现在一个表单中。最后一点不要忘了在Layout文件中讲包含上述javascript片断的js文件包含进来。
1: @model Person
2: @using (Html.BeginForm())
3: {
4: @Html.EditorForModel()
5: <input type="submit" value="Save" />6: }
运行我们的程序,输入不合法出生日期并点击”Save”按钮提交表单(针对第一次客户端验证),客户端验证将会生效,具体效果如下图所示。
自定义验证
在第6章中我们编写了MaxWordsAttribute验证特性来验证一个字符串中的单词个数。其实现代码如下:
可以这样使用这个特性,如下面的代码所示,但是这个特性只能提供服务器端的验证支持:
- public class MaxWordsAttribute : ValidationAttribute
- {
- public MaxWordsAttribute(int maxWords)
- :base("Too many words in {0}")
- {
- MaxWords = maxWords;
- }
- public int MaxWords { get; set; }
- protected override ValidationResult IsValid(
- object value,
- ValidationContext validationContext)
- {
- if (value != null)
- {
- var wordCount = value.ToString().Split(' ').Length;
- if (wordCount > MaxWords)
- {
- return new ValidationResult(
- FormatErrorMessage(validationContext.DisplayName)
- );
- }
- }
- return ValidationResult.Success;
- }
- }
- [Required(ErrorMessage = "An Album Title is required")]
- [StringLength(160)]
- [MaxWords(10)]
- public string Title { get; set; }
为了支持客户端验证,需要让特性实现下面即将讨论的接口。
1. IClientValidatable
IClientValidatable接口定义了一个单一的方法:GetClientValidationRules。当ASP.NET MVC框架使用这个接口查找验证对象时,它会调用GetClientValidationRules方法来检索一个ModelClientValidationRule对象序列。这些对象携带有框架发送给客户端的元数据或规则。
可以使用下面的代码为自定义验证器实现该接口:
- public class MaxWordsAttribute : ValidationAttribute,IClientValidatable
- {
- ...
- public IEnumerable<ModelClientValidationRule>
- GetClientValidationRules(
- ModelMetadata metadata, ControllerContext context)
- {
- var rule = new ModelClientValidationRule();
- rule.ErrorMessage = FormatErrorMessage(metadata.GetDisplayName
- ());
- rule.ValidationParameters.Add("wordcount", WordCount);
- rule.ValidationType = "maxwords";
- yield return rule;
- }
- }
要实现在客户端执行验证,需要给出如下几点信息:
如果验证失败,要显示的提示消息。
允许的单词数的范围。
一段用来计算单词数量的JavaScript代码标识。
这些信息就是代码放进返回规则中的内容。请注意,如果需要在客户端触发多种类型的验证,代码可以返回多个规则。
其中,代码将错误提示消息放入规则的ErrorMessage属性中。这样做可以使服务器端错误提示消息精确地匹配客户端错误提示消息。ValidationParameters集合用来存放客户端需要的参数,像允许的最大单词数。如果需要的话,还可以继续向该集合中放其他的参数,但是要注意参数的名称是有意义的,它们需要匹配在客户端脚本中看到的名称。最后,ValidationType属性标识了客户端需要的一段JavaScript代码。
ASP.NET MVC框架在客户端上利用GetClientValidationRules方法返回的规则将信息序列化为data-特性:
- <input
- data-val="true"
- data-val-length="The field Title must be a string with a maximum length
- of 160."
- data-val-length-max="160"
- data-val-maxwords="Too many words in Title"
- data-val-maxwords-wordcount="10"
- data-val-required="An Album Title is required" id="Title" name="Title"
- type="text" value="For Those About To Rock We Salute You" />
注意,maxwords是如何出现在与MaxWords特性有关的特性名称中的呢?maxwords文本之所以会出现在相关特性的名称中,是因为代码将规则的ValidationType属性设置成了maxwords(是的,验证类型和所有的验证参数名称必须都是小写,因为它们的值必须能够作为合法的HTML特性标识符使用)。
尽管现在客户端上有元数据,但是仍然需要编写一些执行验证逻辑的脚本代码。
2. 自定义验证脚本代码
值得庆幸的是,在客户端上没有必要编写代码来从data-特性中挖掘元数据值。然而,为了执行验证工作,需要以下两段脚本代码:
适配器:适配器和非侵入式MVC扩展一道识别需要的元数据。然后非侵入式扩展帮助从data-特性中检索值,并且还帮助把数据转换为jQuery验证能够理解的格式。
验证规则:在jQuery用语中被称作验证器。
这两段代码都在同一个脚本文件中。假设某一时刻想把这两段代码放在MusicScripts.js文件中,该文件是在本章8.1.3节中创建的。在这种情况下,要确保文件MusicScripts.js出现在验证脚本之后。这可以借助于前面创建的脚本节点(scripts section),使用下面的代码来实现:
在MovieScripts.js文件的内部,一些引用提供了所需要的所有智能感知功能:
- @section scripts
- {
- <script src="@Url.Content("~/Scripts/jquery.validate.min.js")"
- type="text/javascript"></script>
- <script
- src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")"
- type="text/javascript"></script>
- <script src="@Url.Content("~/Scripts/MusicScripts.js")"
- type="text/javascript">
- </script>
- }
- /// <reference path="jquery-1.4.4.js" />
- /// <reference path="jquery.validate.js" />
- /// <reference path="jquery.validate.unobtrusive.js" />
首先要编写的代码是适配器。MVC框架的非侵入式验证扩展存储了jQuery.validator. unobtrusive.adapters对象中的所有适配器。这些适配器对象暴露了一个API,我们可以用来添加新的适配器,如表8-2所示。
表8-2 适配器方法
名 称
描 述
addBool
为"启用"或"禁止"的验证规则创建适配器。
该规则不需要额外的参数
addSingleVal
为需要从元数据中检索唯一参数值的验证
规则创建适配器
addMinMax
创建一个映射到验证规则集的适配器——一个用来检
查最小值,另一个用来检查最大值。这两个规则中
至少有一个要依靠得到的数据运行
Add
创建一个不适合前面类别的适配器,因为它需要
额外的参数,或额外的设置代码
对于最大单词数的情形,可以使用addSingleVal或addMinMax(或add,因为它可以适用于任何场合)。由于不需要检查单词的最小数量,因此可以使用API函数addSingleVal,代码如下所示:
- /// <reference path="jquery-1.4.4.js" />
- /// <reference path="jquery.validate.js" />
- /// <reference path="jquery.validate.unobtrusive.js" />
- $.validator.unobtrusive.adapters.addSingleVal("maxwords", "wordcount");
第一个参数是适配器的名称,它必须与服务器端设置的ValidationProperty值匹配。第二个参数是要从元数据中检索的单一参数的名称。注意该参数名称上面没有使用data-前缀;在服务器上它匹配放入ValidationParameters集合的参数名称。
适配器相对来说比较简单。同样,适配器的主要目标是识别非侵入式扩展要定位的元数据。有了适配器,现在就可以编写验证器。
所有的验证器都在jQuery.validator对象中。与adapters对象类似,validator对象也有一个API函数,可以用来添加新的验证器。该函数的名称是addMethod:
- $.validator.addMethod("maxwords", function (value, element, maxwords) {
- if (value) {
- if (value.split(' ').length > maxwords) {
- return false;
- }
- }
- return true;
- });
该方法中有两个参数:
验证器的名称:默认情况下,验证器的名称要匹配适配器的名称,而适配器的名称又要匹配服务器上ValidationType属性的值。
函数:当验证发生时调用。
验证函数接收了三个参数,并在验证成功时返回true,验证失败时返回false:
函数的第一个参数包含输入值,如专辑的名称。
第二个参数是输入元素,其中包含了要验证的值(在value本身没有提供足够的信息的情况下使用)。
第三个参数包含一个数组中的所有验证参数,在这个示例中包含了单一验证参数(也即最大的单词数量)。
为了将验证代码引入到自己的项目中,可以使用NuGet安装Wrox.ProMvc3.Ajax.CustomClientValidation包。
虽然ASP.NET MVC AJAX辅助方法提供了很多功能,但是有一个jQuery扩展的完整生态系统。下一节探讨选择组。
如果在浏览器中访问站点http://plugin.jquery.com,将会发现上面提供有数千个jQuery扩展。其中一些扩展是图形化导向的,可以使内容以动画的方式显示;其他一些扩展是像日期选择器和网格一样的部件。
使用jQuery插件通常涉及下载插件、解压缩插件,然后将插件添加到项目中的操作。对于一些以NuGet包的形式获得的jQuery插件,我们可以轻松地添加到项目中。许多插件,尤其是面向UI的插件,除了包含至少一个JavaScript文件之外,可能还包含将要使用的图像和样式表。
每一个新的ASP.NET MVC项目都默认带有两个插件:jQuery Validation(前面已经用过)和jQuery UI(马上就要看到)。
自验证
首先我们这个商品模型Product.cs需要实现IValidatableObject接口,然后实现Validate这个方法。
34567891011121314151617181920212223242526272829303132333435363738public
class
Product:IValidatableObject
{
/// <summary>
/// 产品名称
/// </summary>
[DisplayName(
"产品名称"
)]
[AllowHtml]
//允许输入html脚本
[Required]
public
String Name {
get
;
set
; }
/// <summary>
/// 库存数量
/// </summary>
[DisplayName(
"库存"
)]
[Required]
public
int
Inventory {
get
;
set
; }
/// <summary>
/// 销售数量
/// </summary>
[DisplayName(
"售出"
)]
[Required]
public
int
Shipping {
get
;
set
; }
public
IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if
(Shipping > Inventory)
{
yield
return
new
ValidationResult(
"出库数量不能大于库存数量"
,
new
string
[] {
"Shipping"
});
}
}
}
Index.cshtml
3456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960@model 数据验证.Product
@{
Layout =
null
;
}
<!DOCTYPE html>
<html>
<head>
<title>Index</title>
</head>
<body>
<script src=
"http://www.cnblogs.com/Scripts/jquery-1.4.4.min.js"
type=
"text/javascript"
></script>
<script src=
"http://www.cnblogs.com/Scripts/jquery.validate.min.js"
type=
"text/javascript"
></script>
<script src=
"http://www.cnblogs.com/Scripts/jquery.validate.unobtrusive.min.js"
type=
"text/javascript"
></script>
@
using
(Html.BeginForm()) {
@Html.ValidationSummary(
true
)
<fieldset>
<legend>Product</legend>
<div
class
=
"editor-label"
>
@Html.LabelFor(model => model.Name)
</div>
<div
class
=
"editor-field"
>
@Html.EditorFor(model => model.Name)
@Html.ValidationMessageFor(model => model.Name)
</div>
<div
class
=
"editor-label"
>
@Html.LabelFor(model => model.Inventory)
</div>
<div
class
=
"editor-field"
>
@Html.EditorFor(model => model.Inventory)
@Html.ValidationMessageFor(model => model.Inventory)
</div>
<div
class
=
"editor-label"
>
@Html.LabelFor(model => model.Shipping)
</div>
<div
class
=
"editor-field"
>
@Html.EditorFor(model => model.Shipping)
@Html.ValidationMessageFor(model => model.Shipping)
</div>
<p>
<input type=
"submit"
value=
"Create"
/>
</p>
</fieldset>
}
<div>
@Html.ActionLink(
"Back to List"
,
"Index"
)
</div>
</body>
</html>