ASP.NET MVC 3: 如何写一个支持unobtrusive客户端验证的Email Validation Attribute

ASP.NET MVC 3引入了对Unobtrusive Javascript的支持,结合jQuery Valiator插件,让客户端验证的Javascript代码与Html剥离开,使得页面代码更清晰简洁。关于如何使用,请猛击这里

当需要验证一个Email字段是否合法,只需像ASP.NET MVC 2那样,使用RegularExpressionAttribute修饰一下即可。

[RegularExpression(@"[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?", ErrorMessage = "Invalid Email address.")]
public string Email { get; set; }

ASP.NET MVC 会自动与jQuery Validator绑定,实现客户端的验证。而需要重用这个Email的验证时,只需自己写一个定制的EmailValidationAttribute

public class EmailAttribute : RegularExpressionAttribute
{
    public EmailAttribute()
        : base(@"[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?")
    { }
}

并用它来代替RegualrExpressionAttribute加到属性上面。

[Email(ErrorMessage = "Invalid Email address.")]
public string Email { get; set; }

这样做看似没有问题,但运行后发现,只有服务器端验证,而客户端验证并没有工作,这是为什么呢?
原来,ASP.NET MVC 3是通过IClientValidatable这个新的接口来实现客户端验证的,也就是说所有的要支持客户端验证的Attribute必须实现这个接口。
但RegualrExpressionAttribute并不是定义在MVC里面的,而是随着.NET 4.0发布的,放在System.ComponentModel.DataAnnotations.dll这个assembly里面,那他是如何实现客户端验证的呢?
查看一下MVC的源代码,发现其为这几个著名的DataAnnotation加了个Adapter。

typeof(RegularExpressionAttribute),
    (metadata, context, attribute) => new RegularExpressionAttributeAdapter(metadata, context, (RegularExpressionAttribute)attribute)

而这Adapter做的事情,正是IClientValidatable接口需要做的,返回其ModelClientValidationRule。

public override IEnumerable<ModelClientValidationRule> GetClientValidationRules() {
    return new[] { new ModelClientValidationRegexRule(ErrorMessage, Attribute.Pattern) };
}

所以,相应的,把这个正则表达式的ModelClientValidationRule加到Email验证里面就大功告成了。

public class EmailAttribute : RegularExpressionAttribute, IClientValidatable
{
    public EmailAttribute()
    : base(@"[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?")
    { }

    public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
    {
        return new[] { new ModelClientValidationRegexRule(this.ErrorMessage, this.Pattern) };
    }
}

Charles Vallance有一个直接的解决方案,定义一个ModelClientValidationRule,并将ValidationType设置为“email”。这样做也可以,但有一个问题。实际上,ValidationType对应的是jQuery Validator中定义的email验证方法

email: function(value, element) {
    // contributed by Scott Gonzalez: http://projects.scottsplayground.com/email_address_validation/
    return this.optional(element) || /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$/i.test(value);
},

查看jQuery Validator的源代码就可以发现,其使用的正则表达式比我们在服务器端使用的要复杂得多。此时,客户端验证与服务器端验证并不统一,留下后患。

Posted in ASP.NET MVC | Tagged , | Leave a comment

ASP.NET MVC 3: CS1973编译错误

今天在写代码的时候碰到一个CS1973编译错误。

代码如下:

<%: Html.TextBox("Hello", ViewBag.Value) %>

发现问题了吗?下面是错误描述:

Compiler Error Message: CS1973: ‘System.Web.Mvc.HtmlHelper<object>’ has no applicable method named ‘TextBox’ but appears to have an extension method by that name. Extension methods cannot be dynamically dispatched. Consider casting the dynamic arguments or calling the extension method without the extension method syntax.

通过描述发现,问题是出在ASP.NET MVC 3新引入的ViewBag上面,由于它实际是一个dynamic类型,而dynamic类型又不能使用扩展方面(Extension Method)。因为扩展方面是编译时的,是将扩展的类型传入静态方法。但dynamic的机制是运行时的,他需要运行时解析数据类型并调用其方法或属性。如果将dynamic类型传入扩展方法,编译器将无法选择合适的重载方法,将抛出此编译错误,注意是编译错误

所以修复这问题的方法就是加一个类型转换,告诉编译器我们要调用哪个重载。

<%: Html.TextBox("Hello", (string)ViewBag.Value) %>

参考:

Posted in ASP.NET MVC | Tagged , | Leave a comment

换成了Thematic皮肤

四处闲逛,发现一个Best Word Press Themes站点,推荐了很多优秀的主题,包括免费的收费的。其中有个top10,排名第一的就是Thematic,预览了一下截图,感觉很好,是我想要的淡雅的风格。遂下载安装设置,哇默认竟然提供了这么多的container,甚是满意,Theme主页http://themeshaper.com/thematic/

这款Theme的优秀之处,并不在于它的配色插图设计等,而是它提供了一个框架,可以让主题使用者很容易的在上面做二次设计(Design),正好适合我这种WP入门者,引用作者的话:

Thematic is a free, open-source, highly extensible, search-engine optimized WordPress Theme Framework featuring 13 widget-ready areas, grid-based layout samples, styling for popular plugins, and a whole community behind it. It’s perfect for beginner bloggers and WordPress development professionals.

并且作者在此主题框架的基础上,已经做出许多个扩展主题,并且也可以把自己扩展的主题,会发给作者,优秀者会在页面上展示出来。这让我突然想起了ZenGarden,web design界的MOMO熊,拿一张画板,让想象力去填充。

Posted in 站务相关 (Site) | Tagged , | 1 Comment

Visual David .Net 正式开张

经过几周的调研, 一天的艰苦奋斗,终于把域名主机跟wordpress都倒弄起来了。初看上去,效果还凑合,用了Benoît «LeBen» Burgener 的Smooth主题,灰蒙的淡雅看起来不错。 可惜的是作者用的一个文字特效很难支持中文这种大字符集(因为要将文字动态转换为位图),又自己改了一下,而且同样的字体,中文字总是看起来小一点,看来css应该支持不同字符集的字体设置。希望能找到一个好的免费的素雅的模板。

Gallery用了下载量超高的NextGEN,不过对wordpress还不太熟悉,不晓得怎么优雅的把它们link出来,改天再弄吧。

用了SyntaxHighligher,来显示代码,先试用试用, 贴一段儿:

//Hello.cs
using System;
public static void Main(){
    Console.WriteLine("Hello World, I'm Visual David .Net");
}
(function foo(){
alert("Hello World, I'm Visual David .Net");
})();
Posted in 站务相关 (Site) | Tagged | 5 Comments
Highslide for Wordpress Plugin