带过下传统的ASP.NET MVC程序中的模型验证。在MVC程序中的视图模型是一种具有自我描述能力的实体类,而这个自我描述的能力是可以我们开发人员进行扩展的。在这些描述信息中,很重要的一部分就是对模型本身进行校验的信息,这些信息可以供框架校验用户反馈的模型的合法性,当然这个校验的过程在后台是少不了的。当然前台的校验工作也不能缺少,为了让用户有更好的体验以及减轻服务器端不必要的校验工作,ASP.NET MVC会将一部分的校验工作放在前台界面,让js来承担这部分的责任。这就是传统的模型验证的大概,详细的,可以参考MicrosoftMvcValidation.debug.js的代码以及在.aspx页面中输出的模型验证元数据。
使用传统的模型验证,不仅具有很好的可扩展性,恐怕其易用性以及简洁的代码的威力也是不可小觑的,相信有接触过的人都有感受。用过ExtJS的可能也有接触过extjs的表单验证,对于它的优点及用法我不是很清楚,总不至于是个差的东西,应该是不错的。尽管它可能很好用,但是它终归是一个独立的框架,无法直接去使用ASP.NET MVC为MicrosoftMvcValidation.debug.js生成的前台模型验证元数据。有一个很明显的事实那就是前台页面上已经有了模型验证元数据了,这些元数据是一些很有价值的东西,我总得想个办法使用它吧,1、要么打extjs的主意,扩展它的验证器;2、要么就是打MicrosoftMvcValidation的主意,让它的验证结果最终变成extjs的验证结果。出于对extjs庞大的体系的恐惧,我最终选择了后者。我重写了MicrosoftMvcValidation.debug.js中的Sys.Mvc.FieldContext.prototype._displayError,让原本的显示逻辑转换为extjs的呈现错误的逻辑即可,很干脆!
2
3 Ext.form.Field.prototype.msgTarget = ' side ' ;
4 Ext.form.Field.prototype.validationDelay = 0 ;
5 Ext.form.Field.prototype.validateOnBlur = false ;
6 Ext.form.Field.prototype.validationEvent = ' keydown ' ;
7 Sys.Mvc.FieldContext.prototype._displayError = function () {
8 if ( this ._errors.length > 0 ) {
9 Ext.getCmp($( this .elements).attr( ' id ' )).markInvalid( this ._errors[ 0 ]);
10 }
11 }
Ext.QuickTips.init();Ext.form.Field.prototype.msgTarget = 'side';Ext.form.Field.prototype.validationDelay = 0;Ext.form.Field.prototype.validateOnBlur = false;Ext.form.Field.prototype.validationEvent = 'keydown';Sys.Mvc.FieldContext.prototype._displayError = function () { if (this._errors.length > 0) { Ext.getCmp($(this.elements).attr('id')).markInvalid(this._errors[0]); }}
到现在还完全不够,因为extjs的表单的是用Ext.form.FormPanel来构建的,而ASP.NET MVC只会在Html.EndForm()的时候为这个form进行表单域的验证信息的输出,这完全是不符合extjs的需求的。我们需要一种手段,不需要在Html.EndForm()的时候输出一段验证信息,而是指定为某个表单输出某个或者某几个视图模型的验证信息,那么extjs的FormPanel才能很好的工作。这需要额外的工作来为extjs以另外一种不同于ASP.NET MVC原生的模型验证元数据输出方式输出验证信息。下面是一个辅助类,有两个扩展方法
2 /// 输出验证信息的辅助类
3 /// </summary>
4 public static class ValidateHelper
5 {
6 private const string _clientValidationScript = @" <script type=""text/javascript"">
7 //<![CDATA[
8 if (!window.mvcClientValidationMetadata) {{ window.mvcClientValidationMetadata = []; }}
9 //]]>
10 </script> " ;
11 private const string _clientMetadatas = @" <script type=""text/javascript"">
12 //<![CDATA[
13 window.mvcClientValidationMetadata.push({0});
14 //]]>
15 </script> " ;
16 private const string _enableKey = " *&^enable*UY^ " ;
17 // 开启客户端验证
18 public static void EnableExtJsValidation( this HtmlHelper html)
19 {
20 var httpContext = HttpContext.Current;
21 if ( ! httpContext.Items.Contains(_enableKey))
22 {
23 httpContext.Items[_enableKey] = true ;
24 httpContext.Response.Write(_clientValidationScript);
25 }
26 }
27
28 /// <summary>
29 /// 向客户端输出模型验证元数据
30 /// </summary>
31 /// <param name="formId"> 需要进行验证的表单 </param>
32 /// <param name="modelTypes"> 提供元数据的视图模型的类型 </param>
33 public static void OutputClientValidationMetadata( this HtmlHelper html, string formId, params Type[] modelTypes)
34 {
35 if (HttpContext.Current.Items[_enableKey] != null &&
36 HttpContext.Current.Items[_enableKey] is bool &&
37 ( bool )HttpContext.Current.Items[_enableKey])
38 {
39
40 FormContext formContext = new FormContext {FormId = formId};
41 html.ViewContext.FormContext = formContext;
42 var dataAnnotationsModelMetadataProvider = new DataAnnotationsModelMetadataProvider();
43 foreach (var modelType in modelTypes)
44 {
45 var metadatas = dataAnnotationsModelMetadataProvider.GetMetadataForType( null , modelType);
46 foreach (var prop in metadatas.Properties)
47 {
48 var fieldMetadata = formContext.GetValidationMetadataForField(prop.PropertyName, true );
49 var validators = ModelValidatorProviders.Providers.GetValidators(prop, html.ViewContext);
50 foreach (
51 ModelClientValidationRule rule in validators.SelectMany(v => v.GetClientValidationRules()))
52 {
53 fieldMetadata.ValidationRules.Add(rule);
54 }
55 }
56 }
57
58 html.ViewContext.HttpContext.Response.Write( string .Format(_clientMetadatas,
59 formContext.GetJsonValidationMetadata()));
60 }
61 }
62 }
public static class ValidateHelper{ private const string _clientValidationScript = @"<script type=""text/javascript"">//<![CDATA[if (!window.mvcClientValidationMetadata) {{ window.mvcClientValidationMetadata = []; }}//]]></script>"; private const string _clientMetadatas= @"<script type=""text/javascript"">//<![CDATA[window.mvcClientValidationMetadata.push({0});//]]></script>"; private const string _enableKey = "*&^enable*UY^"; //开启客户端验证 public static void EnableExtJsValidation(this HtmlHelper html) { var httpContext = HttpContext.Current; if (!httpContext.Items.Contains(_enableKey)) { httpContext.Items[_enableKey] = true; httpContext.Response.Write(_clientValidationScript); } } /// <summary> /// 向客户端输出模型验证元数据 /// </summary> /// <param name="formId">需要进行验证的表单</param> /// <param name="modelTypes">提供元数据的视图模型的类型</param> public static void OutputClientValidationMetadata(this HtmlHelper html, string formId, params Type[] modelTypes) { if (HttpContext.Current.Items[_enableKey] != null && HttpContext.Current.Items[_enableKey] is bool && (bool)HttpContext.Current.Items[_enableKey]) { FormContext formContext = new FormContext {FormId = formId}; html.ViewContext.FormContext = formContext; var dataAnnotationsModelMetadataProvider = new DataAnnotationsModelMetadataProvider(); foreach (var modelType in modelTypes) { var metadatas = dataAnnotationsModelMetadataProvider.GetMetadataForType(null, modelType); foreach (var prop in metadatas.Properties) { var fieldMetadata = formContext.GetValidationMetadataForField(prop.PropertyName, true); var validators = ModelValidatorProviders.Providers.GetValidators(prop, html.ViewContext); foreach ( ModelClientValidationRule rule in validators.SelectMany(v => v.GetClientValidationRules())) { fieldMetadata.ValidationRules.Add(rule); } } } html.ViewContext.HttpContext.Response.Write(string.Format(_clientMetadatas, formContext.GetJsonValidationMetadata())); } }}
OK,一切工作就绪。下面是示例代码
extjs构建formPanel
2 new Ext.form.FormPanel({
3 title: ' form1 ' ,
4 formId: ' form01 ' ,
5 items: [{
6 id: ' Name ' ,
7 fieldLabel: ' 姓名 ' ,
8 xtype: ' textfield '
9 }, {
10 id: ' Age ' ,
11 fieldLabel: ' 年龄 ' ,
12 xtype: ' textfield '
13 }],
14 renderTo: document.body
15 })
16 });
开启客户端验证
2 Html.EnableExtJsValidation();
3 Html.OutputClientValidationMetadata( " form01 " , typeof(StudentModel));
4 %>
视图模型
2 {
3 [Required]
4 [StringLength( 10 ,ErrorMessage = " 姓名的长度不能超过10个字符 " )]
5 public string Name { get ; set ; }
6 [RegularExpression( @" \d{1,3} " ,ErrorMessage = " 年龄必须是3位数字 " )]
7 public int Age { get ; set ; }
8 }
结果:
当然还有很多细节工作需要完善,例如form表单提交时的验证等(可能的话明天再补充)。上面只是一种解决的思路,采用它是因为微软的模型验证机制比较容易扩展,并且这样扩展不仅能应用于传统的ASP.NET MVC的开发同时也可以应用extjs同mvc的结合开发。