ASP.NET MVC加上Entity Framework已经是微软平台下Web开发的利器。通过为实体类添加验证属性,可以非常方便的对用户输入从客户端和服务器端进行双重验证,同时这一套机制还极具扩展性,我们可以用标准方式添加我们自己的验证属性,使其行为和表现与标配的验证属性一致。下面我打算创建一个验证电话号码的验证属性。

这里其实没有什么复杂的逻辑,直接上代码会更清晰。首先我定义了一个PhoneType枚举,用来定义需要验证的电话号码类型。这里暂时只有一种:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace CAF.Business.Validations
{
public enum PhoneType
{
/// <summary>
/// for example 123-456-7890
/// </summary>
Us334Dash
}
}

然后是验证属性类PhoneValidationAttribute,在定义实体类的时候就是通过这个属性(Attribute)来标识需要验证电话号码的实体类属性(Property)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel.DataAnnotations;
using System.Text.RegularExpressions;

namespace CAF.Business.Validations
{
/// <summary>
/// validate phone number, the default format is Us334Dash
/// </summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public class PhoneValidationAttribute : ValidationAttribute
{
public PhoneType PhoneType { get; set; }

public PhoneValidationAttribute()
{
PhoneType = PhoneType.Us334Dash;
}

public override bool IsValid(object value)
{
if (value == null)
return true;

string phone = value.ToString();

if (phone == string.Empty)
return true;

string validationPattern = string.Empty;
switch (PhoneType)
{
case PhoneType.Us334Dash:
default:
validationPattern = @"^\d{3}-\d{3}-\d{4}$";
break;
}

Regex regex = new Regex(validationPattern);

return regex.IsMatch(phone);
}
}
}

需要说明的是,利用PhoneValidationAttribute类我们只能做到服务器端验证。那如何做到客户端验证呢?那需要一些额外的步骤。

首先,我们需要一个ModelValidator类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Mvc;
using CAF.Business.Validations;

namespace Synvata.Web.Validations
{
public class PhoneModelValidator : DataAnnotationsModelValidator<PhoneValidationAttribute>
{
private readonly PhoneType _PhoneType;
private readonly string _ErrMsg;

public PhoneModelValidator(ModelMetadata metadata, ControllerContext context, PhoneValidationAttribute attribute)
: base(metadata, context, attribute)
{
_PhoneType = attribute.PhoneType;
if (string.IsNullOrEmpty(attribute.ErrorMessage) == false)
_ErrMsg = attribute.ErrorMessage;
else
_ErrMsg = string.Format("{0} is not a valid phone number. Please enter in this format: nnn-nnn-nnnn", metadata.GetDisplayName());
}

public override IEnumerable<ModelClientValidationRule> GetClientValidationRules()
{
ModelClientValidationRule rule = new ModelClientValidationRule();
rule.ErrorMessage = _ErrMsg;
rule.ValidationType = "validatephone";
rule.ValidationParameters.Add("phonetype", _PhoneType.ToString());
yield return rule;
}
}
}

定义这个类的时候需要用到前面的PhoneValidationAttribute类,另外注意,定义默认的错误信息时,用到了metadata.GetDisplayName(),这样可以取到实体类属性上定义的”Display”属性(Attribute)的值。ModelClientValidationRuleValidationType属性和ValidationParameters是定义客户端jQuery Validation组件相关的验证函数和参数。

然后,我们需要在Global.asax.cs中的Application_Start方法内进行注册:

1
2
3
4
5
6
7
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(PhoneValidationAttribute), typeof(PhoneModelValidator));
}
}

最后就是添加客户端的验证函数,这里我偷懒了,没有用到phonetype参数:

1
2
3
4
5
6
7
8
9
10
$.validator.addMethod("validatephone", function (value, element, phonetype) {
if (this.optional(element))
return true;

var phoneRe = /^\d{3}-\d{3}-\d{4}$/
return phoneRe.test(value);

});

$.validator.unobtrusive.adapters.addSingleVal("validatephone", "phonetype");

这里需要2步,第一步,为jQuery Validation添加新的验证函数,第二步,把前面的验证函数注册到微软扩展的unobtrusive上,至于addSingleVal等方法的使用,可以看看源代码或者Google一下。

至此,大功告成。

留言