分享web开发知识

注册/登录|最近发布|今日推荐

主页 IT知识网页技术软件开发前端开发代码编程运营维护技术分享教程案例
当前位置:首页 > IT知识

MVC数据验证原理及自定义ModelValidatorProvider实现无编译修改验证规则和错误信息

发布时间:2023-09-06 01:18责任编辑:胡小海关键词:MVC编译

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="50" message="FirstName_Length" /> ?<validator property="LastName" type="Required" ?message="LastName_Required" /> ?<validator property="LastName" type="StringLength" arg-int="255" message="LastName_Length" /> ?<validator property="Email" type="Required" ?message="Email_Required" /> ?<validator property="Email" type="StringLength" arg-int="255" message="Email_Length" /> ?<validator property="Email" type="RegularExpression" arg="^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+$" message="Email_RegularExpression" /> ?<validator property="Url" type="StringLength" arg-int="255" 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 = 0; ??????????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(‘-‘)[1]; ??????????????????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实现无编译修改验证规则和错误信息

原文地址:http://www.cnblogs.com/webenh/p/7691766.html

我的编程学习网——分享web前端后端开发技术知识。 垃圾信息处理邮箱 tousu563@163.com 网站地图
icp备案号 闽ICP备2023006418号-8 不良信息举报平台 互联网安全管理备案 Copyright 2023 www.wodecom.cn All Rights Reserved