Asp.net MVC中的提供非常简单易用的数据验证解决方案. 通过System.ComponentModel.DataAnnotations提供的很多的验证规则(Required, StringLength等)。但是常常有这样的需求,我们希望能够把model的验证规则,保存到数据或者xml文件中,而不是代码里, 这样的好处是,我们可以很方便的修改验证规则和错误消息,避免需要重新发布网站。

这篇文章,一起来看看是如何通过自定义ModelValidatorProvider来通过XML文件配置对于Model的验证。

阅读目录:

一、简单回顾内置MVC验证的使用

二、分析MVC验证的内部过程

三、一个例子,针对ContactInfo的验证

四、具体实现和应用XmlModelValidatorProvider

一,简单回顾内置MVC验证的使用

下面是典型的MVC验证规则的代码和页面展示效果。

二,分析MVC验证的内部过程

1. 实际做验证的是ModelValidator

当我们如上图,在为Person类添加了各种验证规则的dataannotation attributes后,实际操刀来做验证的是DataAnnotationsModelValidator类.
DataAnnotationsModelValidator继承自抽象类ModelValidator,实现了抽象方法Validate, 在该方法中根据Person类中定义的验证规则,对于所有Person的实例进行验证,同时返回一个ModelValidationResult的集合。

2. ModelValidator是由ModelValidatorProviders提供的

MVC在验证过程中使用到的ModelValidator又是由ModelValidatorProviders类提供的, ModelValidatorProviders是一个抽象类,有个抽象方法GetValidators.

该类的定义是这样的

namespace System.Web.Mvc
{
public abstract class ModelValidatorProvider
{
public abstract IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, ControllerContext context);
}
}

实际运行中,MVC使用的是继承于ModelValidatorProvider, 实现了GetValidators方法的DataAnnotationsModelValidatorProvider类.

3. MVC的验证过程中可以存在多个ModelValidatorProvider

MVC中可以多个ModelValidatorProvider同时起作用, 他们的效果可以叠加。我们可以使用默认的根据Attribute来定义验证规则的DataAnnotationsModelValidatorProvider,也还可以同时使用我们下面的XmlModelValidatorProvider从xml文件中获取验证规则来做验证。

三,一个例子,针对ContactInfo的验证

下面这个ContactInfo类是我们用来做实际验证的,包含了一些常用的典型的验证,Required, Email, Url等

public class ContactInfo
{
public string FirstName { get; set; }
public string LastName{get;set;}
public string Email{get;set;}
public string Url{get;set;}
}

下面的这个xml文件,定义的是关于ContactInfo类的验证规则,以前我们是写在ContactInfo类中的,现在把它分离出来,放到Content\Validation\Rules\ContactInfo.xml文件中.

这里的message中的值只是一个message的key, message的具体内容放在另外一个xml文件中。

<?xml version="1.0" encoding="utf-8" ?>
<model>
<validator property="FirstName" type="Required" message="FirstName_Required" />
<validator property="FirstName" type="StringLength" arg-int="" message="FirstName_Length" />
<validator property="LastName" type="Required" message="LastName_Required" />
<validator property="LastName" type="StringLength" arg-int="" message="LastName_Length" />
<validator property="Email" type="Required" message="Email_Required" />
<validator property="Email" type="StringLength" arg-int="" message="Email_Length" />
<validator property="Email" type="RegularExpression" arg="^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+$" message="Email_RegularExpression" />
<validator property="Url" type="StringLength" arg-int="" message="Url_Length" />
<validator property="Url" type="RegularExpression" arg="(http://)?(www\.)?\w+\.(com|net|edu|org)" message="Url_RegularExpression" />
</model>

存放message的是Content\Validation\Messages\ContactInfo.xml文件

<?xml version="1.0" encoding="utf-8" ?>
<messages>
<!-- filed error message -->
<message key="FirstName_Required" text="The Frist Name field is required."></message>
<message key="FirstName_Length" text="The field maximum length is 50"></message>
<message key="LastName_Required" text="The Last Name field is required."></message>
<message key="LastName_Length" text="The field maximum length is 255"></message>
<message key="Email_Required" text="The Email field is required."></message>
<message key="Email_Length" text="The field maximum length is 255"></message>
<message key="Email_RegularExpression" text="Invalid email."></message>
<message key="Url_Length" text="The field maximum length is 255"></message>
<message key="Url_RegularExpression" text="Invalid URL."></message>
</messages>

四,具体实现和应用XmlModelValidatorProvider

下面是我们最重要的XmlModelValidatorProvider的实现代码

public class XmlModelValidatorProvider : ModelValidatorProvider
{
// 用来保存System.ComponentModel.DataAnnotations中已经存在的验证规则,也就是MVC自带的Required等验证规则, 因为我们只是验证规则的"源"不一样,一个是代码中,一个是xml,但是验证过程是一样的,所以要重用MVC中的已经写好的验证。
private readonly Dictionary<string, Type> _validatorTypes; private readonly string _xmlFolderPath = HttpContext.Current.Server.MapPath("~//Content//Validation//Rules"); public XmlModelValidatorProvider()
{
_validatorTypes = Assembly.Load("System.ComponentModel.DataAnnotations").GetTypes()
.Where(t => t.IsSubclassOf(typeof (ValidationAttribute)))
.ToDictionary(t => t.Name, t => t);
} #region Stolen from DataAnnotationsModelValidatorProvider
// delegate that converts ValidationAttribute into DataAnnotationsModelValidator
internal static DataAnnotationsModelValidationFactory DefaultAttributeFactory =
(metadata, context, attribute) => new DataAnnotationsModelValidator(metadata, context, attribute); internal static Dictionary<Type, DataAnnotationsModelValidationFactory> AttributeFactories = new Dictionary
<Type, DataAnnotationsModelValidationFactory>
{
{
typeof (RangeAttribute),
( metadata, context, attribute)
=>
new RangeAttributeAdapter (metadata, context, ( RangeAttribute ) attribute)
},
{
typeof (RegularExpressionAttribute),
( metadata, context, attribute)
=>
new RegularExpressionAttributeAdapter (metadata, context, ( RegularExpressionAttribute ) attribute)
},
{
typeof (RequiredAttribute),
( metadata, context, attribute) =>
new RequiredAttributeAdapter (metadata, context, ( RequiredAttribute ) attribute)
},
{
typeof (StringLengthAttribute),
( metadata, context, attribute) =>
new StringLengthAttributeAdapter (metadata, context, ( StringLengthAttribute ) attribute)
}
}; #endregion
// 重写GetValidators方法,该从xml文件中获取。根据xml的配置,返回对应的Validator集合
public override IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, ControllerContext context)
{
var results = new List<ModelValidator>(); // whether the validation is for a property or model
// (remember we can apply validation attributes to a property or model and same applies here as well)
var isPropertyValidation = metadata.ContainerType != null && !String.IsNullOrEmpty(metadata.PropertyName); var rulesPath = String.Format("{0}\\{1}.xml", _xmlFolderPath,
isPropertyValidation ? metadata.ContainerType.Name : metadata.ModelType.Name); var rules = File.Exists(rulesPath)
? XElement.Load(rulesPath).XPathSelectElements(String.Format(
"./validator[@property='{0}']",
isPropertyValidation ? metadata.PropertyName : metadata.ModelType.Name)).ToList()
: new List<XElement>(); // Produce a validator for each validation attribute we find
foreach (var rule in rules)
{
DataAnnotationsModelValidationFactory factory; var validatorType = _validatorTypes[String.Concat(rule.Attribute("type").Value, "Attribute")]; if (!AttributeFactories.TryGetValue(validatorType, out factory))
{
factory = DefaultAttributeFactory;
} var validator = (ValidationAttribute) Activator.CreateInstance(validatorType, GetValidationArgs(rule));
validator.ErrorMessage = rule.Attribute("message") != null &&
!String.IsNullOrEmpty(rule.Attribute("message").Value)
? GetValidationMessage(isPropertyValidation ? metadata.ContainerType.Name : metadata.ModelType.Name, rule.Attribute("message").Value)
: null;
results.Add(factory(metadata, context, validator));
} return results;
} private string GetValidationMessage(string model, string key)
{
return MessageProvider.GetViewModelValidationMessage(model, key);
} // read the arguments passed to the validation attribute and cast it their respective type.
private object[] GetValidationArgs(XElement rule)
{
var validatorArgs = rule.Attributes().Where(a => a.Name.ToString().StartsWith("arg"));
var args = new object[validatorArgs.Count()];
var i = ; foreach (var arg in validatorArgs)
{
var argName = arg.Name.ToString();
var argValue = arg.Value; if (!argName.Contains("-"))
{
args[i] = argValue;
}
else
{
var argType = argName.Split('-')[]; switch (argType)
{
case "int":
args[i] = int.Parse(argValue);
break; case "datetime":
args[i] = DateTime.Parse(argValue);
break; case "char":
args[i] = Char.Parse(argValue);
break; case "double":
args[i] = Double.Parse(argValue);
break; case "decimal":
args[i] = Decimal.Parse(argValue);
break; case "bool":
args[i] = Boolean.Parse(argValue);
break; default:
args[i] = argValue;
break;
}
}
} return args;
}
}

最后,在Global.cs文件中,把XmlModelValidatorProvider添加到MVC的ModelValidatorProvidersCollection中

ModelValidatorProviders.Providers.Add(new XmlModelValidatorProvider());

MVC数据验证原理及自定义ModelValidatorProvider实现无编译修改验证规则和错误信息的更多相关文章

  1. Spring MVC内容协商实现原理及自定义配置【享学Spring MVC】

    每篇一句 在绝对力量面前,一切技巧都是浮云 前言 上文 介绍了Http内容协商的一些概念,以及Spring MVC内置的4种协商方式使用介绍.本文主要针对Spring MVC内容协商方式:从步骤.原理 ...

  2. 自定义ModelValidatorProvider

    MVC数据验证原理及自定义ModelValidatorProvider实现无编译修改验证规则和错误信息 Asp.net MVC中的提供非常简单易用的数据验证解决方案. 通过System.Compone ...

  3. egg框架实现表单验证及获取验证的错误信息

    最近再看egg框架 是阿里今年开源的一款基于nodejs的mvc架构的web框架 感兴趣的同学可以看看 因为之前一直做php的关系 在tp,yii,laravel这些框架中对于前台发送的数据,模型里都 ...

  4. asp.net mvc源码分析-DefaultModelBinder 自定义的普通数据类型的绑定和验证

    原文:asp.net mvc源码分析-DefaultModelBinder 自定义的普通数据类型的绑定和验证 在前面的文章中我们曾经涉及到ControllerActionInvoker类GetPara ...

  5. MVC 数据验证

    MVC 数据验证 前一篇说了MVC数据验证的例子,这次来详细说说各种各样的验证注解.System.ComponentModel.DataAnnotations 一.基础特性 一.Required 必填 ...

  6. 转:MVC 数据验证

    一.基础特性 一.Required 必填选项,当提交的表单缺少该值就引发验证错误. 二.StringLength 指定允许的长度 指定最大长度: [StringLength()] //最大长度不超过2 ...

  7. MVC 数据验证[转]

    前一篇说了MVC数据验证的例子,这次来详细说说各种各样的验证注解. 一.基础特性 一.Required 必填选项,当提交的表单缺少该值就引发验证错误. 二.StringLength 指定允许的长度 指 ...

  8. MVC 数据验证【转】

    [转自]http://www.cnblogs.com/dozer/archive/2010/04/12/MVC-DataAnnotations.html 作者Dozer 今天在这里给大家介绍一下MVC ...

  9. MVC数据验证

    深入浅出 MVC 数据验证 2.0 [附演示源码] 今天在这里给大家介绍一下MVC的数据验证框架. 在1.0版中,很多朋友提出了怎么使用客户端验证,今天找了一些资料,发现了客户端验证的方法. 1.MV ...

随机推荐

  1. iOs 自定义UIView 日历的实现 Swift2.1

    学习Swift有一个月了,动手写一个UIView吧. 所有源代码在最后,直接用就可以了,第一次写Swift,和C#,Java还是有区别的 (博客园可以考虑在代码插入中添加Swift的着色了) 1  函 ...

  2. jquery简单原则器(匹配索引为指定值的元素)

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  3. 【Java每日一题】20161130

    20161129问题解析请点击今日问题下方的"[Java每日一题]20161130"查看 package Nov2016; public class Ques1130 { publ ...

  4. OpenWRT学习笔记-1@WNDR3800

    很久以前买了一台二手wndr3800 放假了刷成op可以一耍.ar71xx,芯片还比较热门. 首先是刷机,准备使用OP CC 15.05 r46767,官方稳定版.把原装系统降级到1.0.0.16,就 ...

  5. EffectiveJava——请不要在代码中使用原生态类型

    先看一个栗子,看看能不能找出来里面的错误: /** * 请不要在新代码中使用原生态类型 * @author weishiyao * */ public class Test { public stat ...

  6. 一个java实习生两周八次的面试经历

    以前从来没有因为求职出去面试过,一直觉得面试很可怕,没想到最近两周我也成为了面霸,两周面试八次,我的找工作之路就这样开始了!大概两个星期之前,我看着自己在招聘网站上写好的简历连投出去的勇气都没有,战战 ...

  7. Several ports (8080, 8009) required by Tomcat v6.0 Server at localhost are already in use.

    遇到这个问题似然重启是个办法,但是真心好累.. 1.在dos下,输入  netstat   -ano|findstr  8080 //说明:查看占用8080端口的进程 显示占用端口的进程 2.在任务管 ...

  8. 15款美丽的设备模板,帮助展示你的 APP

    有什么更好的方式来显示您的应用程序的外观和感觉呢?你看了这些设计示例之后就会有灵感了.无论是在 iPhone,iPad,Android,还是在桌面应用程序或网站,这些设计都增加了一些视觉上的吸引力,并 ...

  9. CSS3背景温故

    1.背景的五种基本属性background-color(背景颜色)background-image(背景图片)background-repeat(背景图片展示方式)background-attachm ...

  10. github添加ssh key报错Key is invalid. Ensure you've copied the file correctly

    github添加ssh key的时候报错:Key is invalid. Ensure you've copied the file correctly 将秘钥复制粘贴到文本编辑器中,再粘贴复制到