通过前面的介绍我们知道ModelValidatorProviders的静态只读Providers维护着一个全局的ModelValidatorProvider列表,最终用于Model验证的ModelValidator都是通过这些ModelValidatorProvider来提供的。对于该列表默认包含的三种ModelValidatorProvider来说,DataAnnotationsModelValidatorProvider无疑是最重要的,ASP.NET MVC默认提供的基于数据标注特性的声明式Model验证就是通过DataAnnotationsModelValidatorProvider提供的DataAnnotationsModelValidator来实现的。
一、ValidationAttribute特性
与通过数据标注特性定义Model元数据类似,我们可以在作为Model的数据类型及其属性上应用相应的标注特性来定义Model验证规则。所有的验证特性都直接或者间接继承自抽象类型System.ComponentModel.DataAnnotations.ValidationAttribute。如下面的代码片断所示,ValidationAttribute具有一个字符串类型的ErrorMessage属性用于指定验证错误消息。出于对本地化或者对错误消息单独维护的需要,我们可以采用资源文件的方式来保存错误消息,在这种情况下我们只需要通过ErrorMessageResourceName和ErrorMessageResourceType这两个属性指定错误消息所在资源项的名称和类型即可。
1: public abstract class ValidationAttribute : Attribute 2: { 3: public string ErrorMessage { get; set; } 4: public string ErrorMessageResourceName { get; set; } 5: public Type ErrorMessageResourceType { get; set; } 6: protected string ErrorMessageString {get;} 7: 8: public virtual string FormatErrorMessage(string name); 9: 10: public virtual bool IsValid(object value); 11: protected virtual ValidationResult IsValid(object value, ValidationContext validationContext) 12: 13: public void Validate(object value, string name); 14: public ValidationResult GetValidationResult(object value, ValidationContext validationContext); 15: }
二、验证消息的定义
如果我们通过ErrorMessage属性指定一个字符串作为验证错误消息,又通过ErrorMessageResourceName/ErrorMessageResourceType属性指定了错误消息资源项对应的名称和类型,后者具有更高的优先级。ValidationAttribute具有一个受保护的只读属性ErrorMessageString用于返回最终的错误消息文本。
对于错误消息的定义,我们可以定义一个完整的消息,比如“年龄必需在18至25之间”。但是对于像资源文件这种对错误消息进行独立维护的情况,为了让定义的资源文本能够最大限度地被重用,我们倾向于定义一个包含占位符的文本模板,比如“{DisplayName}必需在{LowerBound}和{UpperBound}之间”,这样消息适用于所有基于数值范围的验证。对于后者,模板中的占位符可以在虚方法FormatErrorMessage中进行替换。该方法中的参数name实际上代表的是对应的显示名称,即对应ModelMetadata的DisplayName属性。
FormatErrorMessage方法在ValidationAttribute中的默认实现仅仅是简单地调用String的静态方法Format将参数name作为替换占位符的参数,具体的定义如下。所以在默认的情况下,我们在定义错误消息模板的时候,只允许包含唯一一个针对显示名称的占位符“{0}”。如果具有额外的占位符,或者不需要采用基于序号(“{0}”)的定义方法(比如采用类似于“{DisplayName}”这种基于文字的占位符更具可读性),只需要重写FormatErrorMessage方法即可。
1: public abstract class ValidationAttribute : Attribute 2: { 3: //其他成员 4: public virtual string FormatErrorMessage(string name) 5: { 6: return string.Format(CultureInfo.CurrentCulture, ErrorMessageString, new object[] { name }); 7: } 8: }
三、验证的执行
当我们通过继承ValidationAttribute创建我们自己的验证特性的时候,可以通过重写公有方法IsValid或者受保护方法IsValid来实现我们自定义的验证逻辑。我们之所以能够通过重写任一个IsValid方法是我们自定义验证逻辑生效的原因在于这两个方法在ValidationAttribute特殊的定义方法。按照这两个方法在ValidationAttribute中的定义,它们之间存在相互调用的关系,而这种相互调用必然造成“死循环”,所以我们需要重写至少其中一个方法比避免“死循环”的方法。这里的“死循环”被加上的引号,是因为ValidationAttribute在内部作了处理,当这种情况出现的时候会抛出一个NotImplementedException异常。
1: //调用公有IsValid方法 2: public class ValidatorAttribute : ValidationAttribute 3: { 4: static void Main() 5: { 6: ValidatorAttribute validator = new ValidatorAttribute(); 7: validator.IsValid(new object()); 8: } 9: } 10: 11: //调用受保护IsValid方法 12: public class ValidatorAttribute : ValidationAttribute 13: { 14: static void Main() 15: { 16: ValidatorAttribute validator = new ValidatorAttribute(); 17: validator.IsValid(new object(),null); 18: } 19: }
为了验证对虚方法IsValid重写的必要性,我们来做一个简单的实例演示。在一个控制台应用中我们分别编写了如上两段程序,其中通过继承ValidationAttribute定义了一个ValidatorAttribute,但是没有重写任何一个IsValid方法。当我们在Debug模式下分别运行这两段程序的时候,都会抛出如下图所示的NotImplementedException异常,提示“此类尚未实现 IsValid(object value)。首选入口点是 GetValidationResult(),并且类应重写 IsValid(object value, ValidationContext context)。”
以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索验证
, 方法
, 调用自定义资源
, 消息
, public
, ValidationAttribute
, 一个
MVC自定义错误
,以便于您获取更多的相关知识。