NET MVC 条件验证

如何使用数据注释对模型进行条件验证?

例如,假设我们有以下模型(Person 和 Advanced) :

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)%>

我想成为。“ Description”属性条件所需字段基于选择的“ Issenid”属性(true-> 必需)。如何使用数据注释在 ASP.NET MVC 2中实现条件验证?

163458 次浏览

您需要在 Person 级别进行验证,而不是高级别,否则高级别必须有对其父 Person 的引用。在我看来,您需要一种自我验证机制,它定义 Person 上的验证,而不是它的某个属性上的验证。我不确定,但是我不认为 DataAnnotions 支持这种开箱即用的方式。您可以创建自己的从 ValidationAttribute派生的 Attribute,它可以在类级别上修饰,然后创建一个自定义验证器,该验证器还允许那些类级别的验证器运行。

我知道验证应用程序块支持开箱即用的自我验证,但是 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));
}
}
}

我已经通过处理控制器包含的 “模范州”字典解决了这个问题。ModelState 字典包括所有必须验证的成员。

解决办法如下:

如果你需要实现一个基于某个字段的 条件验证(例如,如果 A = true,那么 B 是必需的) ,同时维护属性级错误消息(这对于对象级别的自定义验证器来说是不正确的) ,你可以通过处理“ ModelState”来实现这一点,只需删除不需要的验证。

在某些课堂上。

public bool PropertyThatRequiredAnotherFieldToBeFilled
{
get;
set;
}


[Required(ErrorMessage = "*")]
public string DepentedProperty
{
get;
set;
}

继续上课。

在某些控制器动作中。

if (!PropertyThatRequiredAnotherFieldToBeFilled)
{
this.ModelState.Remove("DepentedProperty");
}

...

通过这种方法,我们实现了条件验证,而其他所有东西都保持不变。


更新:

这是我的最后一个实现: 我在模型和 action 属性上使用了一个接口来验证实现了这个接口的模型。接口规定了验证(ModelStateDictionary modelState)方法。Action 属性只是调用 IValidatorSomething 上的 Validate (model State)。

我不想把这个问题复杂化,所以我没有提到最终的实现细节(最后,这在生产代码中很重要)。

您可以通过删除 ModelState 中的错误来有条件地禁用验证程序:

ModelState["DependentProperty"].Errors.Clear();

看看 Simon Ince 的 MVC 中的条件验证

我现在正在做他的示例项目。

谢谢 梅里特:)

我刚刚更新到 MVC3,以防有人发现它有用: ASP.NET MVC 3中的条件验证

有一种更好的方法可以在 MVC3中添加条件验证规则; 让您的模型继承 IValidatableObject并实现 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.");
}
}

详情请浏览 介绍 ASP.NET MVC 3(预览1)

我昨天也遇到了同样的问题,但是我用一种非常干净的方式解决了,这种方式既适用于客户端验证,也适用于服务器端验证。

条件: 基于模型中其他属性的值,您希望创建所需的另一个属性。这是密码

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 (属性)的特定值,必须对其他属性进行必要的验证

假设你有以下信息

public class User
{
public UserType UserType { get; set; }


[RequiredIf("UserType", UserType.Admin, ErrorMessageResourceName = "PasswordRequired", ErrorMessageResourceType = typeof(ResourceString))]
public string Password
{
get;
set;
}
}

最后但并非最不重要的是,为属性注册适配器,以便它能够进行客户端验证(我将其放在 global.asax、 Application _ Start 中)

 DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(RequiredIfAttribute),typeof(RequiredAttributeAdapter));

现在有一个开箱即用的框架来执行这种条件验证(包括其他方便的数据注释验证) : Http://foolproof.codeplex.com/

具体来说,看一下[ RequredIfTrue (“ Isseni”)]验证器。您将其直接放置在您想要验证的属性上,这样您就可以得到与“老年人”属性相关联的验证错误的期望行为。

它以 NuGet 软件包的形式提供。

我有同样的问题,需要修改[必需]属性-使字段需要依赖于 http 请求。这个解决方案类似于 Dan Hunex 的回答,但是他的解决方案不能正确工作(见注释)。我不使用不引人注目的验证,只是使用 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; }

对我来说,主要的好处是我不必像在不引人注目的验证情况下那样编写自定义客户端验证器。它的工作方式和[必需的]一样,但只适用于您想要的情况。

从模型状态中有条件删除错误的典型用法:

  1. 控制器操作的条件第一部分
  2. 执行逻辑从 ModelState 中删除错误
  3. 完成现有逻辑的其余部分(通常是模型状态验证,然后是所有其他部分)

例如:

public ActionResult MyAction(MyViewModel vm)
{
// perform conditional test
// if true, then remove from ModelState (e.g. ModelState.Remove("MyKey")


// Do typical model state validation, inside following if:
//     if (!ModelState.IsValid)


// Do rest of logic (e.g. fetching, saving

在您的示例中,保持一切正常,并将建议的逻辑添加到控制器操作中。我假设您传递给控制器操作的 ViewModel 具有 Person 和 Advanced Person 对象,其中包含来自 UI 的数据。

我一直在使用这个神奇的小技巧,它可以做动态注释 表达式注释

你可以验证任何你能想到的逻辑:

public string Email { get; set; }
public string Phone { get; set; }
[RequiredIf("Email != null")]
[RequiredIf("Phone != null")]
[AssertThat("AgreeToContact == true")]
public bool? AgreeToContact { get; set; }

我使用的是 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; }

在你的例子中,你会说“ Isseni= = true”。 然后你只需要检查你的后期行动的验证。