programing

RequiredIf 조건부 유효성 검사 속성

yoursource 2021. 1. 17. 12:23
반응형

RequiredIf 조건부 유효성 검사 속성


다음을 수행하는 유효성 검사 속성을 구현하는 가장 좋은 방법에 대한 조언을 찾고있었습니다.

모델

public class MyInputModel 
{
    [Required]
    public int Id {get;set;}

    public string MyProperty1 {get;set;}
    public string MyProperty2 {get;set;}
    public bool MyProperty3 {get;set;}

}

적어도 prop1 prop2 prop3에 값이 있고 prop3이 유일한 값이면 false와 같지 않아야합니다. 이것에 대한 유효성 검사 속성을 작성하려면 어떻게해야합니까?

도움을 주셔서 감사합니다!


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

조건 : 모델의 다른 속성 값을 기준으로 다른 속성을 필요로합니다. 다음은 코드입니다.

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 enum UserType
{
  Admin,
  Regular
}

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

편집 됨

어떤 사람들은 클라이언트 측이 작동하지 않거나 작동하지 않더라도 발생한다고 불평했습니다. 그래서 위의 코드를 수정하여 자바 스크립트로 조건부 클라이언트 측 유효성 검사도 수행했습니다. 이 경우 어댑터를 등록 할 필요가 없습니다.

 public class RequiredIfAttribute : ValidationAttribute, IClientValidatable
    {
        private String PropertyName { get; set; }
        private Object DesiredValue { get; set; }
        private readonly RequiredAttribute _innerAttribute;

        public RequiredIfAttribute(String propertyName, Object desiredvalue)
        {
            PropertyName = propertyName;
            DesiredValue = desiredvalue;
            _innerAttribute = new RequiredAttribute();
        }

        protected override ValidationResult IsValid(object value, ValidationContext context)
        {
            var dependentValue = context.ObjectInstance.GetType().GetProperty(PropertyName).GetValue(context.ObjectInstance, null);

            if (dependentValue.ToString() == DesiredValue.ToString())
            {
                if (!_innerAttribute.IsValid(value))
                {
                    return new ValidationResult(FormatErrorMessage(context.DisplayName), new[] { context.MemberName });
                }
            }
            return ValidationResult.Success;
        }

        public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
        {
            var rule = new ModelClientValidationRule
            {
                ErrorMessage = ErrorMessageString,
                ValidationType = "requiredif",
            };
            rule.ValidationParameters["dependentproperty"] = (context as ViewContext).ViewData.TemplateInfo.GetFullHtmlFieldId(PropertyName);
            rule.ValidationParameters["desiredvalue"] = DesiredValue is bool ? DesiredValue.ToString().ToLower() : DesiredValue;

            yield return rule;
        }
    }

그리고 마지막으로 자바 스크립트 (번들 및 렌더링 ... 자체 스크립트 파일에 넣습니다)

$.validator.unobtrusive.adapters.add('requiredif', ['dependentproperty', 'desiredvalue'], function (options) {
    options.rules['requiredif'] = options.params;
    options.messages['requiredif'] = options.message;
});

$.validator.addMethod('requiredif', function (value, element, parameters) {
    var desiredvalue = parameters.desiredvalue;
    desiredvalue = (desiredvalue == null ? '' : desiredvalue).toString();
    var controlType = $("input[id$='" + parameters.dependentproperty + "']").attr("type");
    var actualvalue = {}
    if (controlType == "checkbox" || controlType == "radio") {
        var control = $("input[id$='" + parameters.dependentproperty + "']:checked");
        actualvalue = control.val();
    } else {
        actualvalue = $("#" + parameters.dependentproperty).val();
    }
    if ($.trim(desiredvalue).toLowerCase() === $.trim(actualvalue).toLocaleLowerCase()) {
        var isValid = $.validator.methods.required.call(this, value, element, parameters);
        return isValid;
    }
    return true;
});

요구 사항으로 포함하려면 눈에 거슬리지 않는 검증 jquery가 필요합니다.


나는 그 주제가 얼마 전에 질문되었다는 것을 알고 있지만 최근에 비슷한 문제에 직면하여 또 다른 문제를 발견했지만 내 의견으로는 더 완전한 해결책을 찾았습니다. 조건부 속성을 제공하는 메커니즘을 구현하여 논리식에 정의 된 다른 속성 값과 이들 간의 관계를 기반으로 유효성 검사 결과를 계산하기로 결정했습니다.

이를 사용하면 다음과 같은 방식으로 요청한 결과를 얻을 수 있습니다.

[RequiredIf("MyProperty2 == null && MyProperty3 == false")]
public string MyProperty1 { get; set; }

[RequiredIf("MyProperty1 == null && MyProperty3 == false")]
public string MyProperty2 { get; set; }

[AssertThat("MyProperty1 != null || MyProperty2 != null || MyProperty3 == true")]
public bool MyProperty3 { get; set; }

ExpressiveAnnotations 라이브러리에 대한 자세한 정보는 여기에서 찾을 수 있습니다 . 추가 사례 별 속성을 작성하거나 컨트롤러 내부에서 명령 적 유효성 검사 방법을 사용할 필요없이 많은 선언적 유효성 검사 사례를 단순화해야합니다.


ASP.NET MVC 5 에서 작동하도록했습니다.

이 코드에 관심이 있고 고통받는 많은 사람들을 보았고 처음으로 정말 혼란스럽고 혼란 스럽습니다.

메모

  • 서버와 클라이언트 측 모두에서 MVC 5에서 일했습니다 : D
  • "ExpressiveAnnotations"라이브러리를 전혀 설치하지 않았습니다.
  • " @Dan Hunex " 의 원본 코드에 대해 알아 보겠습니다. 위에서 찾아보세요.

이 오류를 수정하는 팁

"System.Web.Mvc.RequiredAttributeAdapter 유형에는 System.Web.Mvc.ModelMetadata, System.Web.Mvc.ControllerContext 및 ExpressiveAnnotations.Attributes.RequiredIfAttribute 매개 변수 이름 : adapterType 유형의 세 가지 매개 변수를 허용하는 공용 생성자가 있어야합니다."

팁 # 1 : ' RequiredAttribute '가 아닌 ' ValidationAttribute ' 에서 상속하고 있는지 확인하십시오 .

 public class RequiredIfAttribute : ValidationAttribute, IClientValidatable { ...}

팁 # 2 : 또는 ' Global.asax ' 에서이 전체 줄을 제거 합니다. 최신 버전의 코드에서는 전혀 필요하지 않습니다 (@Dan_Hunex로 편집 한 후). 예,이 줄은 이전 버전에서 필수 항목이었습니다. .

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

Javascript 코드 부분 작업을 얻는 팁

1- 코드를 새 js 파일 (예 : requiredIfValidator.js)에 넣습니다.

2- $ (document) .ready (function () {........});

3- JQuery 유효성 검사 라이브러리를 포함시킨 후 js 파일을 포함하면 이제 다음과 같이 보입니다.

@Scripts.Render("~/bundles/jqueryval")
<script src="~/Content/JS/requiredIfValidator.js"></script>

4- C # 코드 편집

...에서

rule.ValidationParameters["dependentproperty"] = (context as ViewContext).ViewData.TemplateInfo.GetFullHtmlFieldId(PropertyName);

...에

rule.ValidationParameters["dependentproperty"] = PropertyName;

그리고

if (dependentValue.ToString() == DesiredValue.ToString())

...에

if (dependentValue != null && dependentValue.ToString() == DesiredValue.ToString())

내 전체 코드 실행 및 실행

Global.asax

여기에 추가 할 항목이 없습니다. 깨끗하게 유지하세요.

requiredIfValidator.js

~ / content 또는 ~ / scripts 폴더에이 파일을 만듭니다.

    $.validator.unobtrusive.adapters.add('requiredif', ['dependentproperty', 'desiredvalue'], function (options)
{
    options.rules['requiredif'] = options.params;
    options.messages['requiredif'] = options.message;
});


$(document).ready(function ()
{

    $.validator.addMethod('requiredif', function (value, element, parameters) {
        var desiredvalue = parameters.desiredvalue;
        desiredvalue = (desiredvalue == null ? '' : desiredvalue).toString();
        var controlType = $("input[id$='" + parameters.dependentproperty + "']").attr("type");
        var actualvalue = {}
        if (controlType == "checkbox" || controlType == "radio") {
            var control = $("input[id$='" + parameters.dependentproperty + "']:checked");
            actualvalue = control.val();
        } else {
            actualvalue = $("#" + parameters.dependentproperty).val();
        }
        if ($.trim(desiredvalue).toLowerCase() === $.trim(actualvalue).toLocaleLowerCase()) {
            var isValid = $.validator.methods.required.call(this, value, element, parameters);
            return isValid;
        }
        return true;
    });
});

_Layout.cshtml 또는보기

@Scripts.Render("~/bundles/jqueryval")
<script src="~/Content/JS/requiredIfValidator.js"></script>

RequiredIfAttribute.cs 클래스

예를 들어 ~ / models / customValidation /에서 프로젝트의 어느 곳에 든 생성하십시오.

    using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;

namespace Your_Project_Name.Models.CustomValidation
{
    public class RequiredIfAttribute : ValidationAttribute, IClientValidatable
    {
        private String PropertyName { get; set; }
        private Object DesiredValue { get; set; }
        private readonly RequiredAttribute _innerAttribute;

        public RequiredIfAttribute(String propertyName, Object desiredvalue)
        {
            PropertyName = propertyName;
            DesiredValue = desiredvalue;
            _innerAttribute = new RequiredAttribute();
        }

        protected override ValidationResult IsValid(object value, ValidationContext context)
        {
            var dependentValue = context.ObjectInstance.GetType().GetProperty(PropertyName).GetValue(context.ObjectInstance, null);

            if (dependentValue != null && dependentValue.ToString() == DesiredValue.ToString())
            {
                if (!_innerAttribute.IsValid(value))
                {
                    return new ValidationResult(FormatErrorMessage(context.DisplayName), new[] { context.MemberName });
                }
            }
            return ValidationResult.Success;
        }

        public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
        {
            var rule = new ModelClientValidationRule
            {
                ErrorMessage = ErrorMessageString,
                ValidationType = "requiredif",
            };
            rule.ValidationParameters["dependentproperty"] = PropertyName;
            rule.ValidationParameters["desiredvalue"] = DesiredValue is bool ? DesiredValue.ToString().ToLower() : DesiredValue;

            yield return rule;
        }
    }
}

모델

    using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Web;
using Your_Project_Name.Models.CustomValidation;

namespace Your_Project_Name.Models.ViewModels
{

    public class CreateOpenActivity
    {
        public Nullable<int> ORG_BY_CD { get; set; }

        [RequiredIf("ORG_BY_CD", "5", ErrorMessage = "Coordinator ID is required")] // This means: IF 'ORG_BY_CD' is equal 5 (for the example)  > make 'COR_CI_ID_NUM' required and apply its all validation / data annotations
        [RegularExpression("[0-9]+", ErrorMessage = "Enter Numbers Only")]
        [MaxLength(9, ErrorMessage = "Enter a valid ID Number")]
        [MinLength(9, ErrorMessage = "Enter a valid ID Number")]
        public string COR_CI_ID_NUM { get; set; }
    }
}

보기

여기서 실제로 주목할 것은 없습니다 ...

    @model Your_Project_Name.Models.ViewModels.CreateOpenActivity
@{
    ViewBag.Title = "Testing";
}

@using (Html.BeginForm()) 
{
    @Html.AntiForgeryToken()

    <div class="form-horizontal">
        <h4>CreateOpenActivity</h4>
        <hr />
        @Html.ValidationSummary(true, "", new { @class = "text-danger" })

        <div class="form-group">
            @Html.LabelFor(model => model.ORG_BY_CD, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.ORG_BY_CD, new { htmlAttributes = new { @class = "form-control" } })
                @Html.ValidationMessageFor(model => model.ORG_BY_CD, "", new { @class = "text-danger" })
            </div>
        </div>

        <div class="form-group">
            @Html.LabelFor(model => model.COR_CI_ID_NUM, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.COR_CI_ID_NUM, new { htmlAttributes = new { @class = "form-control" } })
                @Html.ValidationMessageFor(model => model.COR_CI_ID_NUM, "", new { @class = "text-danger" })
            </div>
        </div>

        <div class="form-group">
            <div class="col-md-offset-2 col-md-10">
                <input type="submit" value="Create" class="btn btn-default" />
            </div>
        </div>
    </div>
}

나중에이를 위해 프로젝트 샘플을 업로드 할 수 있습니다 ...

도움이 되었기를 바랍니다.

감사합니다


Adel Mourad 및 Dan Hunex의 메모를 확장 하여 주어진 값과 일치 하지 않는 값만 허용하는 예제를 제공하기 위해 코드를 수정했습니다 .

또한 JavaScript가 필요하지 않다는 것을 알았습니다.

내 Models 폴더에 다음 클래스를 추가했습니다.

public class RequiredIfNotAttribute : ValidationAttribute, IClientValidatable
{
    private String PropertyName { get; set; }
    private Object InvalidValue { get; set; }
    private readonly RequiredAttribute _innerAttribute;

    public RequiredIfNotAttribute(String propertyName, Object invalidValue)
    {
        PropertyName = propertyName;
        InvalidValue = invalidValue;
        _innerAttribute = new RequiredAttribute();
    }

    protected override ValidationResult IsValid(object value, ValidationContext context)
    {
        var dependentValue = context.ObjectInstance.GetType().GetProperty(PropertyName).GetValue(context.ObjectInstance, null);

        if (dependentValue.ToString() != InvalidValue.ToString())
        {
            if (!_innerAttribute.IsValid(value))
            {
                return new ValidationResult(FormatErrorMessage(context.DisplayName), new[] { context.MemberName });
            }
        }
        return ValidationResult.Success;
    }

    public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
    {
        var rule = new ModelClientValidationRule
        {
            ErrorMessage = ErrorMessageString,
            ValidationType = "requiredifnot",
        };
        rule.ValidationParameters["dependentproperty"] = (context as ViewContext).ViewData.TemplateInfo.GetFullHtmlFieldId(PropertyName);
        rule.ValidationParameters["invalidvalue"] = InvalidValue is bool ? InvalidValue.ToString().ToLower() : InvalidValue;

        yield return rule;
    }

뷰를 변경할 필요는 없었지만 모델 속성을 변경했습니다.

    [RequiredIfNot("Id", 0, ErrorMessage = "Please select a Source")]
    public string TemplateGTSource { get; set; }

    public string TemplateGTMedium
    {
        get
        {
            return "Email";
        }
    }

    [RequiredIfNot("Id", 0, ErrorMessage = "Please enter a Campaign")]
    public string TemplateGTCampaign { get; set; }

    [RequiredIfNot("Id", 0, ErrorMessage = "Please enter a Term")]
    public string TemplateGTTerm { get; set; }

도움이 되었기를 바랍니다!


"ModelState.Remove"또는 "ModelState ["Prop "]. Errors.Clear ()"를 사용하려고하면 "ModelState.IsValid"stil이 false를 반환합니다.

Why not just removing the default "Required" Annotation from Model and make your custom validation before the "ModelState.IsValid" on Controller 'Post' action? Like this:

if (!String.IsNullOrEmpty(yourClass.Property1) && String.IsNullOrEmpty(yourClass.dependantProperty))            
            ModelState.AddModelError("dependantProperty", "It´s necessary to select some 'dependant'.");

The main difference from other solutions here is that this one reuses logic in RequiredAttribute on the server side, and uses required's validation method depends property on the client side:

public class RequiredIf : RequiredAttribute, IClientValidatable
{
    public string OtherProperty { get; private set; }
    public object OtherPropertyValue { get; private set; }

    public RequiredIf(string otherProperty, object otherPropertyValue)
    {
        OtherProperty = otherProperty;
        OtherPropertyValue = otherPropertyValue;
    }

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        PropertyInfo otherPropertyInfo = validationContext.ObjectType.GetProperty(OtherProperty);
        if (otherPropertyInfo == null)
        {
            return new ValidationResult($"Unknown property {OtherProperty}");
        }

        object otherValue = otherPropertyInfo.GetValue(validationContext.ObjectInstance, null);
        if (Equals(OtherPropertyValue, otherValue)) // if other property has the configured value
            return base.IsValid(value, validationContext);

        return null;
    }

    public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
    {
        var rule = new ModelClientValidationRule();
        rule.ErrorMessage = FormatErrorMessage(metadata.GetDisplayName());
        rule.ValidationType = "requiredif"; // data-val-requiredif
        rule.ValidationParameters.Add("other", OtherProperty); // data-val-requiredif-other
        rule.ValidationParameters.Add("otherval", OtherPropertyValue); // data-val-requiredif-otherval

        yield return rule;
    }
}

$.validator.unobtrusive.adapters.add("requiredif", ["other", "otherval"], function (options) {
    var value = {
        depends: function () {
            var element = $(options.form).find(":input[name='" + options.params.other + "']")[0];
            return element && $(element).val() == options.params.otherval;
        }
    }
    options.rules["required"] = value;
    options.messages["required"] = options.message;
});

ReferenceURL : https://stackoverflow.com/questions/7390902/requiredif-conditional-validation-attribute

반응형