IT story

ASP.NET MVC 조건부 유효성 검사

hot-time 2020. 7. 11. 09:36
반응형

ASP.NET MVC 조건부 유효성 검사


데이터 주석을 사용하여 모델에서 조건부 유효성 검사를 수행하는 방법은 무엇입니까?

예를 들어 다음과 같은 모델 (개인 및 시니어)이 있다고 가정 해 보겠습니다.

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-> 필수)에 따라 "Senior.Description"속성 조건부 필수 필드가되고 싶습니다. 데이터 주석을 사용하여 ASP.NET MVC 2에서 조건부 유효성 검사를 구현하는 방법은 무엇입니까?


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.");
    }
}

http://weblogs.asp.net/scottgu/archive/2010/07/27/introducing-asp-net-mvc-3-preview-1.aspx 에서 자세한 설명을 참조하십시오


컨트롤러에 포함 된 "ModelState" 사전 을 처리하여이 문제를 해결했습니다 . ModelState 사전에는 유효성을 검사해야하는 모든 멤버가 포함됩니다.

해결책은 다음과 같습니다.

속성 수준 오류 메시지를 유지하면서 일부 필드 (예 : A = true이면 B가 필요)를 기반으로 조건부 유효성 검사 를 구현해야하는 경우 (객체 수준에있는 사용자 지정 유효성 검사기는 해당되지 않음) "ModelState"를 처리하여 원치 않는 유효성 검사를 제거하면됩니다.

... 일부 수업에서 ...

public bool PropertyThatRequiredAnotherFieldToBeFilled
{
  get;
  set;
}

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

... 수업은 계속됩니다 ...

... 일부 컨트롤러 작업에서 ...

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

...

이를 통해 조건부 유효성 검사를 수행하면서 다른 모든 항목은 동일하게 유지합니다.


최신 정보:

이것이 나의 최종 구현이다 : 나는 모델의 인터페이스와 상기 인터페이스를 구현하는 모델을 검증하는 action 속성을 사용했다. 인터페이스는 Validate (ModelStateDictionary modelState) 메소드를 규정합니다. action 속성은 IValidatorSomething에서 Validate (modelState)를 호출합니다.

이 답변을 복잡하게 만들고 싶지 않았으므로 최종 구현 세부 사항 (결국 생산 코드의 문제)에 대해서는 언급하지 않았습니다.


어제 같은 문제가 있었지만 클라이언트 측과 서버 측 유효성 검사 모두에서 작동하는 매우 깨끗한 방식으로 문제를 해결했습니다.

조건 : 모델의 다른 속성 값을 기준으로 다른 속성을 필요로합니다. 여기 코드가 있습니다

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

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)

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

I've been using this amazing nuget that does dynamic annotations 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; }

You can disable validators conditionally by removing errors from ModelState:

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

Thanks Merritt :)

I've just updated this to MVC 3 in case anyone finds it useful; http://blogs.msdn.com/b/simonince/archive/2011/02/04/conditional-validation-in-asp-net-mvc-3.aspx

Simon


There is now a framework that does this conditional validation (among other handy data annotation validations) out of the box: http://foolproof.codeplex.com/

Specifically, take a look at the [RequiredIfTrue("IsSenior")] validator. 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.


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. I'm not sure, but I don't think DataAnnotations supports this out of the box. 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.

I know Validation Application Block supports self-validation out-of the box, but VAB has a pretty steep learning curve. Nevertheless, here's an example using 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));
        }
    }
}

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). I don't use unobtrusive validation, just MicrosoftMvcValidation.js out of the box. 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

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

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.


Check out this guy:

http://blogs.msdn.com/b/simonince/archive/2010/06/04/conditional-validation-in-mvc.aspx

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
  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.


I'm using MVC 5 but you could try something like this:

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". Then you just need to check the validation on your post action.

참고URL : https://stackoverflow.com/questions/2417113/asp-net-mvc-conditional-validation

반응형