MVCデータ検証原理及びカスタムModelValidatorProviderによる無コンパイル修正検証規則とエラー情報の実現
19428 ワード
Asp.NetMVCでは、非常に使いやすいデータ検証ソリューションを提供しています.
この記事では、ModelValidatorProviderをカスタマイズしてXMLファイル構成でModelの検証を行う方法について説明します.
目次を読む:
一、内蔵MVC検証の使用を簡単に振り返る
二、MVC検証の内部過程を分析する
三、一例、ContactInfoの検証
四、XmlModelValidatorProviderの具体的な実現と応用
一、内蔵MVC検証の使用を簡単に振り返る
System.ComponentModel.DataAnnotations
によって提供される多くの検証規則(Required,StringLengthなど).しかし、コードではなくmodelの検証ルールをデータやxmlファイルに保存する必要があることがよくあります.このようなメリットは、検証ルールやエラーメッセージを簡単に修正し、Webサイトを再公開する必要がないようにすることです.この記事では、ModelValidatorProviderをカスタマイズしてXMLファイル構成でModelの検証を行う方法について説明します.
目次を読む:
一、内蔵MVC検証の使用を簡単に振り返る
二、MVC検証の内部過程を分析する
三、一例、ContactInfoの検証
四、XmlModelValidatorProviderの具体的な実現と応用
一、内蔵MVC検証の使用を簡単に振り返る
MVC 。
, MVC
1. ModelValidator
, Person dataannotation attributes , DataAnnotationsModelValidator .
DataAnnotationsModelValidator
クラスModelValidatorから され、Personクラスで された ルールに づいて、すべてのPersonのインスタンスを するとともに、ModelValidationResultのセットを す メソッドValidateが されます.
2.ModelValidatorはModelValidatorProvidersが する
MVCが に するModelValidatorはまたModelValidatorProvidersクラスによって され、ModelValidatorProvidersは クラスであり、 な GetValidatorsがある.
クラスの はこうです
namespace System.Web.Mvc
{
public abstract class ModelValidatorProvider
{
public abstract IEnumerable GetValidators(ModelMetadata metadata, ControllerContext context);
}
}
の ではMVCはModelValidatorProviderに するGetValidatorsメソッドを したDataAnnotationsModelValidatorProviderクラスを いる.
3.MVCの に のModelValidatorProvider
が することができる
MVCでは のModelValidatorProviderが に し,それらの を ねることができる.デフォルトのAttributeに づいて ルールを するD a t a n o t i o n s ModelValidatorProviderを するか、 の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クラスに いていましたが、 はそれを してContentValidationRulesContactInfoに きます.xmlファイルで
ここでのmessageの は1つのmessageのkeyにすぎず、messageの な は のxmlファイルに かれています."1.0" encoding="utf-8" ?>
"FirstName" type="Required" message="FirstName_Required" />
"FirstName" type="StringLength" arg-int="50" message="FirstName_Length" />
"LastName" type="Required" message="LastName_Required" />
"LastName" type="StringLength" arg-int="255" message="LastName_Length" />
"Email" type="Required" message="Email_Required" />
"Email" type="StringLength" arg-int="255" message="Email_Length" />
"Email" type="RegularExpression" arg="^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+$" message="Email_RegularExpression" />
"Url" type="StringLength" arg-int="255" message="Url_Length" />
"Url" type="RegularExpression" arg="(http://)?(www\.)?\w+\.(com|net|edu|org)" message="Url_RegularExpression" />
メッセージを するのはContentValidationMessageContactInfoです.xmlファイル"1.0" encoding="utf-8" ?>
"FirstName_Required" text="The Frist Name field is required.">
"FirstName_Length" text="The field maximum length is 50">
"LastName_Required" text="The Last Name field is required.">
"LastName_Length" text="The field maximum length is 255">
"Email_Required" text="The Email field is required.">
"Email_Length" text="The field maximum length is 255">
"Email_RegularExpression" text="Invalid email.">
"Url_Length" text="The field maximum length is 255">
"Url_RegularExpression" text="Invalid URL.">
、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 AttributeFactories = new Dictionary
{
{
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 GetValidators(ModelMetadata metadata, ControllerContext context)
{
var results = new List();
// 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();
// 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());