通过扩展方法 链式方法 为MVC 3 视图添加验证
.Net 3.0 添加了一个语法糖就是扩展方法,其实扩展方法的本质就是对类的静态方法的调用,虽然扩展方法只是改变了我们写代码的方式,但是如果我们使用得当,可以给我们带来巨大的编码效率的提升。对于C#扩展方法的使用,我就不细说了,贴段代码说明扩展方法的使用,大家就会回忆起来。
1 public static class Extensions 2 { 3 public static string EndWith(this string str) 4 { 5 return str + "@"; 6 } 7 }
对于这段非常简单的代码,相信大家应该会很明白,可以看到扩展方法的使用,就是在一个静态类中定义一个静态方法,在方法参数中添加this。
那么使用扩展方法就更加的简单了。以下代码片段就是扩展方法对于string的扩展,会在页面上显示@
1 string str = ""; 2 Response.Write(str.EndWith());
扩展方法不是本次课题的重点,本次课题的重点是说明如何利用扩展方法来链式的对MVC 3中的页面进行验证。对于Mvc的验证,有很多种方式,其中
我们经常使用的就是Model的验证,这是最直接最有效的我们操作的方式,可以对一个字段进行多个条件的验证。但是这种验证方式有一个弊端,就是我
们不能在页面上直接看到对该字段进行了哪些验证,除非我们去Model层查看,但是通常一个项目是分项目组的,我们不一定负责开发Model层,所以这
就会遇到一个沟通的问题。我们都知道,程序员的沟通成本是很高的。
现在提供另一种进行页面验证的方式,这种方式也可以对一个页面元素进行多个条件的验证,并且是在客户端进行的。要使用这个验证,我们必须了解一
个jquery的插件,那就是jquery.validation.js文件,这个是对元素进行验证的js文件。
还有一个我们必须了解的知识点就是?Mvc 3的Razor 视图直接继承自哪个类?相信很多朋友都会毫不犹豫的说是ViewPage,当然这不能说是错的,因
为Mvc 2 确实是继承自ViewPage基类,但是在Razor 视图下,是继承自WebViewPage类的,如果你的扩展方法扩展的是ViewPage,那么对不起,你
在页面上是无法看到这个扩展方法的,除非继承自WebViewPage。希望大家扩展页面方法的时候注意一下类的继承问题。
说了准备工作,那么我们切入正题,先贴出来代码,然后我们再逐渐的分析。
1 namespace MvcWeb.Extensions 2 { 3 public class JqueryHelper 4 { 5 public ViewContext ViewContext { get; private set; } 6 7 public WebViewPage ViewPage { get; private set; } 8 9 public RouteCollection RouteCollection { get; private set; } 10 11 public JqueryHelper(ViewContext viewContext, WebViewPage page, RouteCollection routeCollection) 12 { 13 this.ViewContext = viewContext; 14 this.ViewPage = page; 15 this.RouteCollection = routeCollection; 16 } 17 18 public JqueryHelper(ViewContext viewContext, WebViewPage viewPage) : this(viewContext, viewPage, RouteTable.Routes) { } 19 } 20 21 public static class JqueryExtensions 22 { 23 public static JqueryHelper Jquery(this WebViewPage page) 24 { 25 var key = typeof(JqueryHelper); 26 var jquery = page.PageData[key] as JqueryHelper; 27 if (jquery == null) 28 { 29 page.PageData[key] = jquery = new JqueryHelper(page.ViewContext, page); 30 } 31 return jquery; 32 } 33 } 34 35 public class JqueryValidations 36 { 37 public WebViewPage Page { get; private set; } 38 public JqueryValidations(WebViewPage page) 39 { 40 this.Page = page; 41 } 42 private Dictionary<string, Dictionary<string, object>> m_rules = new Dictionary<string, Dictionary<string, object>>(); 43 private Dictionary<string, Dictionary<string, object>> m_message = new Dictionary<string, Dictionary<string, object>>(); 44 45 public void Required(string name,string message) 46 { 47 this.AddRuleAndMessage(name, "required", true, message); 48 } 49 50 public void Email(string name, string message) 51 { 52 this.AddRuleAndMessage(name,"email",true,message); 53 } 54 55 public void Number(string name, string message) 56 { 57 this.AddRuleAndMessage(name, "number", true, message); 58 } 59 public void Range(string name,int min,int max, string message) 60 { 61 this.AddRuleAndMessage(name, "range", new int[] { min, max }, message); 62 } 63 public string ToScripts(string form) 64 { 65 JavaScriptSerializer serializer = new JavaScriptSerializer(); 66 StringBuilder builder = new StringBuilder(); 67 68 builder.Append("$("); 69 serializer.Serialize(form, builder); 70 builder.Append(").validate("); 71 serializer.Serialize( 72 new 73 { 74 rules = this.m_rules, 75 messages = this.m_message, 76 onkeyup = false 77 }, builder); 78 builder.Append(");"); 79 80 return builder.ToString(); 81 } 82 83 public ValidationElement Element(string name) 84 { 85 return new ValidationElement(name, this); 86 } 87 private void AddRuleAndMessage(string name, string rule, object value, string message) 88 { 89 if (!this.m_rules.ContainsKey(name)) 90 { 91 this.m_rules[name] = new Dictionary<string, object>(); 92 } 93 this.m_rules[name][rule] = value; 94 95 if (!string.IsNullOrEmpty(message)) 96 { 97 if (!this.m_message.ContainsKey(name)) 98 { 99 this.m_message[name] = new Dictionary<string, object>(); 100 } 101 this.m_message[name][rule] = message; 102 } 103 104 } 105 106 107 } 108 109 public class ValidationElement 110 { 111 public ValidationElement(string name, JqueryValidations validations) 112 { 113 this.Name = name; 114 this.Validations = validations; 115 } 116 117 public string Name { get; private set; } 118 119 public JqueryValidations Validations { get; private set; } 120 121 public ValidationElement Required(string message) 122 { 123 this.Validations.Required(this.Name, message); 124 return this; 125 } 126 127 public ValidationElement Email(string message) 128 { 129 this.Validations.Email(this.Name, message); 130 return this; 131 } 132 133 public ValidationElement Number(string message) 134 { 135 this.Validations.Number(this.Name, message); 136 return this; 137 } 138 139 public ValidationElement Range(int min, int max, string message) 140 { 141 this.Validations.Range(this.Name, min, max, message); 142 return this; 143 } 144 145 } 146 147 public static class JQueryValidationExtensions 148 { 149 public static JqueryValidations Validations(this JqueryHelper jquery) 150 { 151 return jquery.Validations("(default)"); 152 } 153 154 public static JqueryValidations Validations(this JqueryHelper jquery, string name) 155 { 156 var key = typeof(JqueryValidations) + "+" + name; 157 var page = jquery.ViewPage; 158 var validations = page.PageData[key] as JqueryValidations; 159 160 if (validations == null) 161 { 162 page.PageData[key] = validations = new JqueryValidations(page); 163 } 164 165 return validations; 166 } 167 } 168 169 }
在这段代码中定义了JqueryHelper 类,是对WebViewPage的扩展,其中有一个方法ToScript 我们要特别注意一下,这个方法是在页面上显示一段拼
接的js,也就是jquery.validation.js会用到的验证页面元素的js。
具体代码我就不细说了,我介绍一下基本思想吧。首先我们定义了一个JqueryHelper类,然后顶一个JqueryValidation类来定义对页面元素可以进行的
验证。这里面有一个链式方法必须用到的就是每个验证方法返回的结果都是JqueryValidation类的对象,这样我们才可以进行链式操作。
通过Dictionary来存储验证的标签Name属性值,错误信息,以及验证规则,最后根据这些值在ToScript中拼接出可以提交的js。
好了,来看个结果,大家就会更加清楚这个链式方法的用途所在了。
1 @{ 2 Layout = null; 3 } 4 @using MvcWeb.Extensions 5 <!DOCTYPE html> 6 <html> 7 <head> 8 <title>Index</title> 9 <script src="http://www.cnblogs.com/Scripts/jquery-1.5.1.js" type="text/javascript"></script> 10 <script type="text/javascript" src="@Url.Content("http://www.cnblogs.com/Scripts/jquery-1.5.1-vsdoc.js")"></script> 11 <script type="text/javascript" src="@Url.Content("http://www.cnblogs.com/scripts/jquery.validate.js")"></script> 12 </head> 13 <body> 14 <div> 15 <form id="form" action="" method="post"> 16 <input type="text" id="user.Name" name="user.Name" /> 17 @{ this.Jquery().Validations().Required("user.Name","用户名是必填的 亲");} 18 19 <select name="user.Gender"> 20 <option value="">Please select...</option> 21 <option value="1">Male</option> 22 <option value="2">Femle</option> 23 </select> 24 25 @{ this.Jquery().Validations().Required("user.Gender", null);} 26 27 <input type="text" name="user.Email" /> 28 @{this.Jquery().Validations().Element("user.Email").Required("邮箱是必填的").Email("请填写正确的邮箱格式");} 29 <input type="submit" value="提交" /> 30 </form> 31 </div> 32 <script type="text/javascript"> 33 @Html.Raw(this.Jquery().Validations().ToScripts("#form")) 34 </script> 35 </body> 36 </html>
对于在页面中实现验证的方法,我介绍一下咱们应该注意的地方
- 就是我们必须引入对应的js文件,如果我们不引入jquery.validation.js文件,那么验证也是不成功的
- 必须把要验证的元素放到form标签内
- 就是我们在通过链式方法验证元素的时候应该注意顺序,虽然说任何顺序都是可以的,但是我们也要按照业务逻辑来对验证顺序进行调整
- 最后我们通过一个submit按钮来进行提交,可能有人要用ajax等其他方式来提交页面,我可以告诉你,我尝试了一下,这样不会触发前端js的验证
- 最后我们必须要通过ToScript方法来拼接出验证的js,在Mvc中框架会自动进行编码,所以我们要求框架不要编码。如果没有要求框架
不要编码,那么最后得到的js结果就是编码过的js,这不是我们要的结果。具体你可以查看源代码看一下执行结果
最后还是有图有真相
1。初始界面显示
2.如果有些条件不满足,那么就会在界面上显示错误信息,当然这是可定制的。在我
在我调试代码的时候,我发现在IE9下,有时候错误信息显示的不是很实时,在火狐下显示的很正常,可能有些问题需要解决。但是不影响使用。
总结一下,链式方法我们其实也经常看到,在我们使用linq的时候,有时会有很多个方法链接使用,提高了程序的可读性。但是我们在设计可
以用来进行链式方法操作的时候一定要注意的就是所有的链式方法的返回值类型尽量相同,因为这便于我们不需要掌握顺序。有的园友会提出
不同意见,linq语法就不是所有的返回值类型都相同,但是我们要知道,我们在使用linq的时候,方法的调用顺序是很重要的对吧。
强调一下,扩展方法不是真的在原有类中添加了一个方法,而是相当于调用的静态类中的静态方法实现的。
天色已黑,我们还在努力。坚强的我们,肯定会有美好的明天。