ASP.NET MVC有条件的validation
如何使用数据注释在模型上进行条件validation?
例如,假设我们有以下模型(Person和Senior):
public class Person { [Required(ErrorMessage = "*")] public string Name { get; set; } public bool IsSenior { get; set; } public Senior Senior { get; set; } } public class Senior { [Required(ErrorMessage = "*")]//this should be conditional validation, based on the "IsSenior" value public string Description { get; set; } }
和以下看法:
<%= Html.EditorFor(m => m.Name)%> <%= Html.ValidationMessageFor(m => m.Name)%> <%= Html.CheckBoxFor(m => m.IsSenior)%> <%= Html.ValidationMessageFor(m => m.IsSenior)%> <%= Html.CheckBoxFor(m => m.Senior.Description)%> <%= Html.ValidationMessageFor(m => m.Senior.Description)%>
我希望成为基于“IsSenior”属性(true – > required)select的“Senior.Description”属性有条件的必填字段。 如何使用数据注释在ASP.NET MVC 2中实现条件validation?
在MVC3中添加条件validation规则有更好的方法。 让模型inheritanceIValidatableObject并实现Validate方法:
public class Person : IValidatableObject { public string Name { get; set; } public bool IsSenior { get; set; } public Senior Senior { get; set; } public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) { if (IsSenior && string.IsNullOrEmpty(Senior.Description)) yield return new ValidationResult("Description must be supplied."); } }
请参阅http://weblogs.asp.net/scottgu/archive/2010/07/27/introducing-asp-net-mvc-3-preview-1.aspx上的更多说明;
我已经通过处理由控制器包含的“ModelState”字典来解决这个问题。 ModelState字典包含所有必须validation的成员。
这是解决scheme:
如果你需要实现基于某个字段的条件validation (例如,如果A = true,那么B是必需的), 同时保持属性级别的错误消息传递 (对于在对象级别上的自定义validation器,这是不对的),你可以实现这个通过处理“ModelState”,只需从中删除不需要的validation。
…在某些class级…
public bool PropertyThatRequiredAnotherFieldToBeFilled { get; set; } [Required(ErrorMessage = "*")] public string DepentedProperty { get; set; }
…继续…
…在一些控制器的行动…
if (!PropertyThatRequiredAnotherFieldToBeFilled) { this.ModelState.Remove("DepentedProperty"); }
…
有了这个,我们实现了有条件的validation,而把所有的东西都保持一致
更新:
这是我最后的实现:我已经在模型上使用了一个接口,action属性validation了实现上述接口的模型。 接口规定了Validate(ModelStateDictionary modelState)方法。 动作属性只是调用IValidatorSomething上的Validate(modelState)。
我不想让这个答案复杂化,所以我没有提到最后的实现细节(最后是生产代码中的问题)。
我昨天也遇到了同样的问题,但是我以非常干净的方式完成了这个工作,可以在客户端和服务器端进行validation。
条件:根据模型中其他属性的值,您需要创build另一个属性。 这是代码
public class RequiredIfAttribute : RequiredAttribute { private String PropertyName { get; set; } private Object DesiredValue { get; set; } public RequiredIfAttribute(String propertyName, Object desiredvalue) { PropertyName = propertyName; DesiredValue = desiredvalue; } protected override ValidationResult IsValid(object value, ValidationContext context) { Object instance = context.ObjectInstance; Type type = instance.GetType(); Object proprtyvalue = type.GetProperty(PropertyName).GetValue(instance, null); if (proprtyvalue.ToString() == DesiredValue.ToString()) { ValidationResult result = base.IsValid(value, context); return result; } return ValidationResult.Success; } }
这里的PropertyName是你想让你的条件的属性DesiredValue是PropertyName(属性)的特定值,你的其他属性必须根据需要进行validation
假设你有以下几点
public class User { public UserType UserType { get; set; } [RequiredIf("UserType", UserType.Admin, ErrorMessageResourceName = "PasswordRequired", ErrorMessageResourceType = typeof(ResourceString))] public string Password { get; set; } }
最后但并非最不重要的,为你的属性注册适配器,以便它可以做客户端validation(我把它放在global.asax,Application_Start)
DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(RequiredIfAttribute),typeof(RequiredAttributeAdapter));
我一直在使用这个惊人的Nuget做dynamic注释ExpressiveAnnotations
你可以validation你可以梦想的任何逻辑:
public string Email { get; set; } public string Phone { get; set; } [RequiredIf("Email != null")] [RequiredIf("Phone != null")] [AssertThat("AgreeToContact == true")] public bool? AgreeToContact { get; set; }
您可以通过从ModelState中删除错误来有条件地禁用validation器:
ModelState["DependentProperty"].Errors.Clear();
谢谢梅里特:)
我刚刚更新到MVC 3,以防有人发现它有用; http://blogs.msdn.com/b/simonince/archive/2011/02/04/conditional-validation-in-asp-net-mvc-3.aspx
西蒙
现在有一个开箱即用的条件validation框架(以及其他方便的数据注释validation): http : //foolproof.codeplex.com/
具体来说,看看[RequiredIfTrue(“IsSenior”)]validation器。 你直接在你想validation的属性上,所以你得到了与“高级”属性相关联的validation错误所需的行为。
它是作为NuGet包提供的。
您需要在人员层面进行validation,而不是在高级层面进行validation,或者高级人员必须参考其父级人员。 在我看来,你需要一个自定义机制来定义Person上的validation,而不是它的一个属性。 我不确定,但我不认为DataAnnotations支持这个开箱即用。 你可以做什么创build自己的Attribute
,派生自ValidationAttribute
,可以在类级别修饰,然后创build一个自定义validation器,也允许这些类级validation器运行。
我知道validation应用程序块支持开箱即用的自我validation,但VAB的学习曲线非常陡峭。 不过,下面是使用VAB的一个例子:
[HasSelfValidation] public class Person { public string Name { get; set; } public bool IsSenior { get; set; } public Senior Senior { get; set; } [SelfValidation] public void ValidateRange(ValidationResults results) { if (this.IsSenior && this.Senior != null && string.IsNullOrEmpty(this.Senior.Description)) { results.AddResult(new ValidationResult( "A senior description is required", this, "", "", null)); } } }
看看这个家伙:
http://blogs.msdn.com/b/simonince/archive/2010/06/04/conditional-validation-in-mvc.aspx
我正在通过他的示例项目工作。
我有同样的问题,需要根据http请求修改[必需的]属性 – 使字段需要。解决scheme类似于Dan Hunex的答案,但他的解决scheme无法正常工作(请参阅注释)。 我不使用不显眼的validation,只是MicrosoftMvcValidation.js开箱即用。 这里是。 实现您的自定义属性:
public class RequiredIfAttribute : RequiredAttribute { public RequiredIfAttribute(/*You can put here pararmeters if You need, as seen in other answers of this topic*/) { } protected override ValidationResult IsValid(object value, ValidationContext context) { //You can put your logic here return ValidationResult.Success;//I don't need its server-side so it always valid on server but you can do what you need } }
然后,您需要实现您的自定义提供程序,以将其用作global.asax中的适配器
public class RequreIfValidator : DataAnnotationsModelValidator <RequiredIfAttribute> { ControllerContext ccontext; public RequreIfValidator(ModelMetadata metadata, ControllerContext context, RequiredIfAttribute attribute) : base(metadata, context, attribute) { ccontext = context;// I need only http request } //override it for custom client-side validation public override IEnumerable<ModelClientValidationRule> GetClientValidationRules() { //here you can customize it as you want ModelClientValidationRule rule = new ModelClientValidationRule() { ErrorMessage = ErrorMessage, //and here is what i need on client side - if you want to make field required on client side just make ValidationType "required" ValidationType =(ccontext.HttpContext.Request["extOperation"] == "2") ? "required" : "none"; }; return new ModelClientValidationRule[] { rule }; } }
并用一行来修改你的global.asax
DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(RequiredIfAttribute), typeof(RequreIfValidator));
在这里
[RequiredIf] public string NomenclatureId { get; set; }
对我来说主要的好处是,我不必为不引人注目的validation代码定制客户端validation器。 它的工作原理与[必需的]一样,但仅限于您需要的情况。
从模型状态有条件地删除错误的典型用法:
- 制定控制器行动的条件的第一部分
- 执行逻辑从ModelState中删除错误
- 做其余的现有的逻辑(通常是模型状态validation,然后是其他一切)
例:
public ActionResult MyAction(MyViewModel vm) { // perform conditional test // if true, then remove from ModelState (eg ModelState.Remove("MyKey") // Do typical model state validation, inside following if: // if (!ModelState.IsValid) // Do rest of logic (eg fetching, saving
在你的例子中,保持所有的东西,并将build议的逻辑添加到Controller的Action中。 我假设传递给控制器操作的ViewModel具有Person和Senior Person对象,并在UI中填充数据。
我正在使用MVC 5,但你可以尝试这样的事情:
public DateTime JobStart { get; set; } [AssertThat("StartDate >= JobStart", ErrorMessage = "Time Manager may not begin before job start date")] [DisplayName("Start Date")] [Required] public DateTime? StartDate { get; set; }
在你的情况下,你会说“IsSenior == true”。 那么你只需要检查你的发布行动的validation。