joe*_*dev 8 c# validation asp.net-mvc-4
我已经在WebForms工作了多年,但我对.NET的MVC很新.我试图找出如何在运行时将动态验证规则应用于我的模型的成员.出于这个问题的目的,这些是我正在使用的类的简化版本:
public class Device
{
public int Id {get; set;}
public ICollection<Setting> Settings {get; set;}
}
public class Setting
{
public int Id {get; set;}
public string Value {get; set;}
public bool IsRequired {get; set;}
public int MinLength {get; set;}
public int MaxLength {get; set;}
}
Run Code Online (Sandbox Code Playgroud)
在我看来,我将使用每个编辑器遍历Settings集合,并在运行时应用每个Setting实例中包含的验证规则,以实现在编译时使用模型上的DataAnnotations获得的相同客户端和服务器端验证.在WebForms中,我只是将相应的Validator附加到相关字段,但我在MVC4中找不到类似的机制.有没有办法实现这个目标?
我的解决方案是扩展ValidationAttribute类并实现IClientValidatable接口.以下是一个完整的示例,有一些改进空间:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Reflection;
using System.Web.Mvc;
namespace WebApplication.Common
{
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
public class RuntimeRequiredAttribute : ValidationAttribute, IClientValidatable
{
public string BooleanSwitch { get; private set; }
public bool AllowEmptyStrings { get; private set; }
public RuntimeRequiredAttribute(string booleanSwitch = "IsRequired", bool allowEmpytStrings = false ) : base("The {0} field is required.")
{
BooleanSwitch = booleanSwitch;
AllowEmptyStrings = allowEmpytStrings;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
PropertyInfo property = validationContext.ObjectType.GetProperty(BooleanSwitch);
if (property == null || property.PropertyType != typeof(bool))
{
throw new ArgumentException(
BooleanSwitch + " is not a valid boolean property for " + validationContext.ObjectType.Name,
BooleanSwitch);
}
if ((bool) property.GetValue(validationContext.ObjectInstance, null) &&
(value == null || (!AllowEmptyStrings && value is string && String.IsNullOrWhiteSpace(value as string))))
{
return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));
}
return ValidationResult.Success;
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata,
ControllerContext context)
{
object model = context.Controller.ViewData.Model;
bool required = (bool)model.GetType().GetProperty(BooleanSwitch).GetValue(model, null);
if (required)
{
yield return
new ModelClientValidationRequiredRule(
FormatErrorMessage(metadata.DisplayName ?? metadata.PropertyName));
}
else
//we have to return a ModelCLientValidationRule where
//ValidationType is not empty or else we get an exception
//since we don't add validation rules clientside for 'notrequired'
//no validation occurs and this works, though it's a bit of a hack
{
yield return
new ModelClientValidationRule {ValidationType = "notrequired", ErrorMessage = ""};
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
上面的代码将在模型上查找用作验证开关的属性(IsRequired是默认值).如果要用作开关的布尔属性设置为true,则对使用该装饰的属性执行客户端和服务器端验证RuntimeRequiredValdiationAttribute.重要的是要注意,该类假定用于验证开关的模型的任何属性都不会显示给最终用户进行编辑,即这不是RequiredIf验证器.
实际上有另一种实现ValidationAttribute的方法以及客户端验证,如此处所述.为了比较,我在上面所做的IClientValidatable路由由同一作者概述.
请注意,这当前不适用于嵌套对象,例如,如果属性修饰另一个对象包含的对象上的属性,它将无法工作.有一些方法可以解决这个缺点,但到目前为止我没有必要这样做.