在《ASP.NET MVC下的四种验证编程方式》一文中我们介绍了ASP.NET MVC支持的四种服务端验证的编程方式(“手工验证”、“标注ValidationAttribute特性”、“让数据类型实现IValidatableObject或者IDataErrorInfo”),那么在ASP.NET MVC框架内部是如何提供针对这四种不同编程方式的支持的呢?接下来我们就来聊聊这背后的故事。
一、ModelValidator与ModelValidatorProvider
虽然Model绑定的方式因被验证数据类型的差异而有所不同,但是ASP.NET MVC总是使用一个名为ModelValidator的对象来对绑定的数据对象实施验证。所有的ModelValidator类型均继承自具有如下定义的抽象类ModelValidator。它的GetClientValidationRules方法返回一个元素类型为ModelClientValidationRule的集合,而ModelClientValidationRule是对客户端验证规则的封装,我们会在客户端验证部分对其进行详细介绍。
1: public abstract class ModelValidator
2: {
3: //其他成员
4: virtual IEnumerable<ModelClientValidationRule> GetClientValidationRules();
5: abstract IEnumerable<ModelValidationResult> Validate(object container);
6:
7: virtual bool Isrequired { get; }
8: }
针对目标数据的验证是通过调用Validate方法来完成的,该方法的输入参数container表示的正是被验证的对象。正是因为被验证的总是一个复杂类型的对象,后者又被称为一个具有若干数据成员的“容器”对象,所以对应的参数被命名为container。Validate方法表示验证结果的返回值并不是一个简单的布尔值,而是一个元素类型为具有如下定义的ModelValidationResult对象集合。
2: {
string Message { get; set; }
class ValidationResult
5: }
ModelValidator具有一个布尔类型的只读属性Isrequired表示该ModelValidator是否对目标数据进行“必需性”验证(即被验证的数据成员必须具有一个具体的值),该属性默认返回False。我们可以通过应用requiredAttribute特性将某个属性定义成“必需”的数据成员。
我们知道ASP.NET MVC大都采用Provider的模式来提供相应的组件,比如描述Model元数据的ModelMetadata通过对应的ModelMetadataProvider来提供,实现Model绑定的ModelBinder则可以通过对应的ModelBinderProvider来提供,用于实现Model验证的ModelValidator也不例外,它对应的提供者为ModelValidatorProvider,对应的类型继承自具有如下定义的抽象类ModelValidator Provider。
class ModelValidatorProviders
static ModelValidatorProviderCollection Providers { get; }
5:
7: {
9: public ModelValidatorProviderCollection(IList<ModelValidatorProvider> list);
10: public IEnumerable<ModelValidator> GetValidators(ModelMetadata Metadata,ControllerContext context);
11: }
值得一提的是用于描述Model元数据的ModelMetadata类型具有如下一个GetValidators方法,它返回的ModelValidator列表正是利用注册到ModelValidatorProviders静态属性Providers上的ModelValidatorProvider创建的。
class DataAnnotationsModelValidator : ModelValidator
override IEnumerable<ModelClientValidationRule> GetClientValidationRules();
protected internal ValidationAttribute Attribute { get; }
override bool Isrequired { get; }
class ValidatableObjectAdapter : ModelValidator
sealed class DataErrorInfoClassModelValidator : ModelValidator
5: }
8: {
class DataErrorInfoModelValidatorProvider : ModelValidatorProvider