简体   繁体   English

ASP.NET MVC 条件验证

[英]ASP.NET MVC Conditional validation

How to use data annotations to do a conditional validation on model?如何使用数据注释对模型进行条件验证?

For example, lets say we have the following model (Person and Senior):例如,假设我们有以下模型(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;
    }
}

And the following view:以及以下视图:

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

I would like to be the "Senior.Description" property conditional required field based on the selection of the "IsSenior" propery (true -> required).我想成为基于“IsSenior”属性选择的“Senior.Description”属性条件必填字段(true -> required)。 How to implement conditional validation in ASP.NET MVC 2 with data annotations?如何使用数据注释在 ASP.NET MVC 2 中实现条件验证?

There's a much better way to add conditional validation rules in MVC3;在 MVC3 中添加条件验证规则有更好的方法; have your model inherit IValidatableObject and implement the Validate method:让您的模型继承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.");
    }
}

Read more at Introducing ASP.NET MVC 3 (Preview 1) .Introducing ASP.NET MVC 3 (Preview 1) 中阅读更多内容。

I have solved this by handling the "ModelState" dictionary, which is contained by the controller.我通过处理控制器包含的“ModelState”字典解决了这个问题。 The ModelState dictionary includes all the members that have to be validated. ModelState 字典包括所有必须验证的成员。

Here is the solution:这是解决方案:

If you need to implement a conditional validation based on some field (eg if A=true, then B is required), while maintaining property level error messaging (this is not true for the custom validators that are on object level) you can achieve this by handling "ModelState", by simply removing unwanted validations from it.如果您需要实现基于某个字段的条件验证(例如,如果 A=true,则需要 B),同时维护属性级别的错误消息(这对于对象级别的自定义验证器并非如此),您可以实现这一点通过处理“ModelState”,只需从中删除不需要的验证。

...In some class... ......在某些班级......

public bool PropertyThatRequiredAnotherFieldToBeFilled
{
  get;
  set;
}

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

...class continues... ……上课继续……

...In some controller action ... ...在某些控制器操作中...

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

... ...

With this we achieve conditional validation, while leaving everything else the same.有了这个,我们实现了条件验证,同时保持其他一切不变。


UPDATE:更新:

This is my final implementation: I have used an interface on the model and the action attribute that validates the model which implements the said interface.这是我的最终实现:我在模型上使用了一个接口和验证实现所述接口的模型的 action 属性。 Interface prescribes the Validate(ModelStateDictionary modelState) method.接口规定了 Validate(ModelStateDictionary modelState) 方法。 The attribute on action just calls the Validate(modelState) on IValidatorSomething. action 上的属性只是调用 IValidatorSomething 上的 Validate(modelState)。

I did not want to complicate this answer, so I did not mention the final implementation details (which, at the end, matter in production code).我不想让这个答案复杂化,所以我没有提到最终的实现细节(最后,这在生产代码中很重要)。

I had the same problem yesterday but I did it in a very clean way which works for both client side and server side validation.昨天我遇到了同样的问题,但我以一种非常干净的方式解决了这个问题,它适用于客户端和服务器端验证。

Condition: Based on the value of other property in the model, you want to make another property required.条件:基于模型中其他属性的值,您想使另一个属性成为必需。 Here is the code这是代码

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;
    }
}

Here PropertyName is the property on which you want to make your condition DesiredValue is the particular value of the PropertyName (property) for which your other property has to be validated for required这里的 PropertyName 是您想要在其上设置条件的属性 DesiredValue 是 PropertyName(属性)的特定值,您的其他属性必须针对 required 进行验证

Say you have the following说你有以下

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

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

At last but not the least , register adapter for your attribute so that it can do client side validation (I put it in global.asax, Application_Start)最后但并非最不重要的是,为您的属性注册适配器,以便它可以进行客户端验证(我将它放在 global.asax、Application_Start 中)

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

I've been using this amazing nuget that does dynamic annotations ExpressiveAnnotations我一直在使用这个惊人的 nuget 进行动态注释ExpressiveAnnotations

You could validate any logic you can dream of:您可以验证您梦寐以求的任何逻辑:

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 中删除错误来有条件地禁用验证器:

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

Thanks Merritt :)谢谢梅里特:)

I've just updated this to MVC 3 in case anyone finds it useful: Conditional Validation in ASP.NET MVC 3 .我刚刚将其更新为 MVC 3,以防有人发现它有用: ASP.NET MVC 3 中的条件验证

There is now a framework that does this conditional validation (among other handy data annotation validations) out of the box: http://foolproof.codeplex.com/现在有一个框架可以开箱即用地执行此条件验证(以及其他方便的数据注释验证): http : //foolproof.codeplex.com/

Specifically, take a look at the [RequiredIfTrue("IsSenior")] validator.具体来说,看看 [RequiredIfTrue("IsSenior")] 验证器。 You put that directly on the property you want to validate, so you get the desired behavior of the validation error being associated to the "Senior" property.您将其直接放在要验证的属性上,这样您就可以获得与“高级”属性相关联的验证错误的所需行为。

It is available as a NuGet package.它以 NuGet 包的形式提供。

I had the same problem, needed a modification of [Required] attribute - make field required in dependence of http request.The solution was similar to Dan Hunex answer, but his solution didn't work correctly (see comments).我遇到了同样的问题,需要修改 [Required] 属性 - 根据 http 请求生成必需字段。解决方案类似于 Dan Hunex 的答案,但他的解决方案无法正常工作(请参阅评论)。 I don't use unobtrusive validation, just MicrosoftMvcValidation.js out of the box.我不使用不显眼的验证,只是开箱即用的 MicrosoftMvcValidation.js。 Here it is.这里是。 Implement your custom attribute:实现您的自定义属性:

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
    }


}

Then you need to implement your custom provider to use it as an adapter in your global.asax然后您需要实现您的自定义提供程序以将其用作 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 };
      }
}

And modify your global.asax with a line并用一行修改您的 global.asax

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

and here it is这是

[RequiredIf]
public string NomenclatureId { get; set; }

The main advantage for me is that I don't have to code custom client validator as in case of unobtrusive validation.对我来说,主要优点是我不必像不引人注目的验证那样编写自定义客户端验证器。 it works just as [Required], but only in cases that you want.它的工作方式与 [Required] 一样,但仅限于您想要的情况。

You need to validate at Person level, not on Senior level, or Senior must have a reference to its parent Person.您需要在人员级别而不是高级级别进行验证,或者高级必须有对其父人员的引用。 It seems to me that you need a self validation mechanism that defines the validation on the Person and not on one of its properties.在我看来,您需要一种自我验证机制来定义 Person 而不是其属性之一的验证。 I'm not sure, but I don't think DataAnnotations supports this out of the box.我不确定,但我不认为 DataAnnotations 支持开箱即用。 What you can do create your own Attribute that derives from ValidationAttribute that can be decorated on class level and next create a custom validator that also allows those class-level validators to run.您可以做什么创建您自己的Attribute派生自ValidationAttribute可以在类级别进行装饰,然后创建一个自定义验证器,该验证器也允许这些类级别验证器运行。

I know Validation Application Block supports self-validation out-of the box, but VAB has a pretty steep learning curve.我知道验证应用程序块支持开箱即用的自我验证,但 VAB 的学习曲线非常陡峭。 Nevertheless, here's an example using 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));
        }
    }
}

Check out Simon Ince's Conditional Validation in MVC .查看 Simon Ince 在 MVC 中条件验证

I am working through his example project right now.我现在正在完成他的示例项目。

Typical usage for conditional removal of error from Model State:有条件地从模型状态中移除错误的典型用法:

  1. Make conditional first part of controller action使控制器动作的第一部分有条件
  2. Perform logic to remove error from ModelState执行逻辑以从 ModelState 中删除错误
  3. Do the rest of the existing logic (typically Model State validation, then everything else)完成现有逻辑的其余部分(通常是模型状态验证,然后是其他所有内容)

Example:例子:

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

In your example, keep everything as is and add the logic suggested to your Controller's Action.在您的示例中,保持一切不变,并将建议的逻辑添加到您的控制器操作中。 I'm assuming your ViewModel passed to the controller action has the Person and Senior Person objects with data populated in them from the UI.我假设您传递给控制器​​操作的 ViewModel 具有 Person 和 Senior Person 对象,其中包含来自 UI 的数据。

I'm using MVC 5 but you could try something like this:我正在使用 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; }

In your case you would say something like "IsSenior == true".在您的情况下,您会说“IsSenior == true”之类的内容。 Then you just need to check the validation on your post action.然后你只需要检查你的帖子操作的验证。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM