Programing

특정 상황에서 필수 유효성 검사 속성 비활성화

crosscheck 2020. 7. 6. 07:52
반응형

특정 상황에서 필수 유효성 검사 속성 비활성화


특정 컨트롤러 작업에서 필수 유효성 검사 특성을 비활성화 할 수 있는지 궁금합니다. 편집 양식 중 하나에서 사용자가 이전에 이미 지정한 필드의 값을 입력 할 필요가 없기 때문에 이것이 궁금합니다. 그러나 값을 입력 할 때 값을 해시하는 등의 특수 논리를 사용하여 모델을 업데이트하는 논리를 구현합니다.

이 문제를 해결하는 방법에 대한 의견이 있으십니까?

편집 :
네, 클라이언트 유효성 검사는 여기에 문제가 있습니다. 값을 입력하지 않고 양식을 제출할 수 없기 때문입니다.


이 문제는 뷰 모델을 사용하여 쉽게 해결할 수 있습니다. 뷰 모델은 주어진 뷰의 요구에 맞게 특별히 설계된 클래스입니다. 예를 들어 귀하의 경우 다음과 같은 뷰 모델을 가질 수 있습니다.

public UpdateViewView
{
    [Required]
    public string Id { get; set; }

    ... some other properties
}

public class InsertViewModel
{
    public string Id { get; set; }

    ... some other properties
}

해당 컨트롤러 작업에 사용됩니다.

[HttpPost]
public ActionResult Update(UpdateViewView model)
{
    ...
}

[HttpPost]
public ActionResult Insert(InsertViewModel model)
{
    ...
}

클라이언트 측에서 단일 필드에 대한 유효성 검증을 사용하지 않으려면 다음과 같이 유효성 검증 속성을 대체 할 수 있습니다.

@Html.TexBoxFor(model => model.SomeValue, 
                new Dictionary<string, object> { { "data-val", false }})

나는이 질문이 오래 전에 답변되어 왔으며 수락 된 답변이 실제로 작업을 수행한다는 것을 알고 있습니다. 그러나 나를 귀찮게하는 한 가지가 있습니다 : 유효성 검사를 비활성화하기 위해 2 개의 모델 만 복사하면됩니다.

내 제안은 다음과 같습니다.

public class InsertModel
{
    [Display(...)]
    public virtual string ID { get; set; }

    ...Other properties
}

public class UpdateModel : InsertModel
{
    [Required]
    public override string ID
    {
        get { return base.ID; }
        set { base.ID = value; }
    }
}

이런 식으로 클라이언트 / 서버 측 유효성 검사를 신경 쓸 필요가 없으며 프레임 워크는 예상대로 작동합니다. 또한 [Display]기본 클래스에서 속성 을 정의하는 경우에 속성을 다시 정의하지 않아도됩니다 UpdateModel.

그리고이 클래스들을 여전히 같은 방식으로 사용할 수 있습니다 :

[HttpPost]
public ActionResult Update(UpdateModel model)
{
    ...
}

[HttpPost]
public ActionResult Insert(InsertModel model)
{
    ...
}

컨트롤러 작업에서 다음을 사용하여 속성에서 모든 유효성 검사를 제거 할 수 있습니다.

ModelState.Remove<ViewModel>(x => x.SomeProperty);

MVC5에 관한 @Ian 의견

다음은 여전히 ​​가능합니다

ModelState.Remove("PropertyNameInModel");

업데이트 된 API로 정적 타이핑을 잃어 버리는 것이 조금 짜증납니다. HTML 헬퍼 인스턴스를 작성하고 NameExtensions Methods를 사용하여 이전 방식과 유사한 것을 달성 할 수 있습니다.


개인적으로 나는 Darin Dimitrov가 그의 해결책에서 보여준 접근법을 사용하는 경향이 있습니다. 이를 통해 유효성 검사와 함께 데이터 주석 접근 방식을 사용할 수 있으며 각 작업에 해당하는 각 ViewModel에 별도의 데이터 속성이 있습니다. 모델과 뷰 모델 간의 복사 작업량을 최소화하려면 AutoMapper 또는 ValueInjecter를 살펴 보십시오 . 둘 다 각자의 장점이 있으므로 확인하십시오.

IValidatableObject에서 뷰 모델 또는 모델을 파생시키는 다른 방법이 있습니다. 이를 통해 Validate 함수를 구현할 수 있습니다. 유효성 검사에서 유효성 검사 결과 요소 목록을 반환하거나 유효성 검사에서 탐지 한 각 문제에 대해 수익률을 반환 할 수 있습니다.

ValidationResult는 오류 메시지와 필드 이름이 포함 된 문자열 목록으로 구성됩니다. 오류 메시지는 입력 필드 근처의 위치에 표시됩니다.

public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
  if( NumberField < 0 )
  {
    yield return new ValidationResult( 
        "Don't input a negative number", 
        new[] { "NumberField" } );
  }

  if( NumberField > 100 )
  {
    yield return new ValidationResult( 
        "Don't input a number > 100", 
        new[] { "NumberField" } );
  }

  yield break;
}

고객 쪽 양식에 대한 유효성 검사를 비활성화하기 위해 내 연구에 기반한 여러 옵션이 아래에 나와 있습니다. 그들 중 하나가 희망적으로 당신을 위해 일할 것입니다.

옵션 1

나는 이것을 선호하며 이것은 완벽하게 작동합니다.

(function ($) {
    $.fn.turnOffValidation = function (form) {
        var settings = form.validate().settings;

        for (var ruleIndex in settings.rules) {
            delete settings.rules[ruleIndex];
        }
    };
})(jQuery); 

그리고 그것을 호출

$('#btn').click(function () {
    $(this).turnOffValidation(jQuery('#myForm'));
});

옵션 2

$('your selector here').data('val', false);
$("form").removeData("validator");
$("form").removeData("unobtrusiveValidation");
$.validator.unobtrusive.parse("form");

옵션 3

var settings = $.data($('#myForm').get(0), 'validator').settings;
settings.ignore = ".input";

옵션 4

 $("form").get(0).submit();
 jQuery('#createForm').unbind('submit').submit();

옵션 5

$('input selector').each(function () {
    $(this).rules('remove');
});

서버 측

속성을 작성하고 해당 속성으로 조치 메소드를 표시하십시오. 특정 요구에 맞게이를 사용자 정의하십시오.

[AttributeUsage(AttributeTargets.All)]
public class IgnoreValidationAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        var modelState = filterContext.Controller.ViewData.ModelState;

        foreach (var modelValue in modelState.Values)
        {
            modelValue.Errors.Clear();
        }
    }
}

더 나은 접근법은 여기에서 설명 합니다 .mvc 서버 측 유효성 검사를 동적으로 활성화 / 비활성화


내가 생각하는 가장 깨끗한 방법은 클라이언트 측 유효성 검사를 비활성화하고 서버 측에서 다음을 수행해야한다는 것입니다.

  1. ModelState [ "SomeField"]. Errors.Clear (컨트롤러에서 또는 컨트롤러 코드가 실행되기 전에 오류를 제거하기위한 작업 필터를 만듭니다)
  2. 감지 된 문제의 위반을 감지하면 컨트롤러 코드에서 ModelState.AddModelError를 추가하십시오.

여기에 사용자 정의보기 모델조차도 '사전 응답 된'필드의 수가 다양 할 수 있기 때문에 문제를 해결하지 못하는 것 같습니다. 그렇지 않으면 사용자 정의보기 모델이 가장 쉬운 방법 일 수 있지만 위의 기술을 사용하면 유효성 검사 문제를 해결할 수 있습니다.


이것은 의견에서 다른 사람의 대답이었습니다 ... 그러나 실제 답변이어야합니다.

$("#SomeValue").removeAttr("data-val-required")

[Required]속성 이있는 필드를 사용하여 MVC 6에서 테스트

위의 https://stackoverflow.com/users/73382/rob 에서 도난당한 답변


내 모델에 대한 편집 뷰를 생성 할 때이 문제가 발생하여 하나의 필드 만 업데이트하려고합니다.

가장 간단한 방법에 대한 내 솔루션은 다음을 사용하여 두 필드를 넣는 것입니다.

 <%: Html.HiddenFor(model => model.ID) %>
 <%: Html.HiddenFor(model => model.Name)%>
 <%: Html.HiddenFor(model => model.Content)%>
 <%: Html.TextAreaFor(model => model.Comments)%>

주석은 편집보기에서만 업데이트하며 필수 속성이없는 필드입니다.

ASP.NET MVC 3 엔터티


AFAIK 런타임에 속성을 제거 할 수는 없지만 값만 변경하십시오 (예 : readonly true / false) 비슷한 것을 찾으십시오 . 속성을 엉망으로하지 않고 원하는 것을 수행하는 또 다른 방법으로 특정 작업에 대해 ViewModel을 사용하여 다른 컨트롤러에 필요한 로직을 손상시키지 않고 모든 로직을 삽입 할 수 있습니다. 일종의 마법사 (다단계 양식)를 얻으려고하면 이미 컴파일 된 필드를 직렬화하고 TempData를 사용하여 단계를 수행하십시오. (직렬화의 직렬화의 도움을 당신이 사용할 수있는 MVC 선물을 )


@Darin이 말한 것은 내가 추천하는 것입니다. 그러나 실제로 비트, 부울, Guid와 같은 기본 유형에 단순히이 null을 허용 하여이 유형을 사용하는 경우 에도이 방법을 사용할 수 있다고 덧붙입니다 (그리고 주석 중 하나에 대한 응답으로). 이 작업을 수행하면 Required속성 예상대로 작동합니다.

public UpdateViewView
{
    [Required]
    public Guid? Id { get; set; }
    [Required]
    public string Name { get; set; }
    [Required]
    public int? Age { get; set; }
    [Required]
    public bool? IsApproved { get; set; }
    //... some other properties
}

MVC 5부터는 이것을에 추가하면 쉽게 얻을 수 있습니다 global.asax.

DataAnnotationsModelValidatorProvider.AddImplicitRequiredAttributeForValueTypes = false;

웹 API에서 삽입 및 업데이트에 동일한 모델을 사용할 수있는 솔루션을 찾고있었습니다. 내 상황에서 이것은 항상 본문 내용입니다. [Requiered]이 업데이트 방법 인 경우 속성은 생략해야합니다. 내 솔루션에서는 [IgnoreRequiredValidations]메소드 위에 속성을 배치합니다 . 이것은 다음과 같습니다 :

public class WebServiceController : ApiController
{
    [HttpPost]
    public IHttpActionResult Insert(SameModel model)
    {
        ...
    }

    [HttpPut]
    [IgnoreRequiredValidations]
    public IHttpActionResult Update(SameModel model)
    {
        ...
    }

    ...

다른 무엇을해야합니까? 시작시 자체 BodyModelValidator를 작성하고 추가해야합니다. 이것은 HttpConfiguration에 있으며 다음과 같습니다.config.Services.Replace(typeof(IBodyModelValidator), new IgnoreRequiredOrDefaultBodyModelValidator());

using Owin;
using your_namespace.Web.Http.Validation;

[assembly: OwinStartup(typeof(your_namespace.Startup))]

namespace your_namespace
{
    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            Configuration(app, new HttpConfiguration());
        }

        public void Configuration(IAppBuilder app, HttpConfiguration config)
        {
            config.Services.Replace(typeof(IBodyModelValidator), new IgnoreRequiredOrDefaultBodyModelValidator());
        }

        ...

내 자신의 BodyModelValidator는 DefaultBodyModelValidator에서 파생됩니다. 그리고 나는 'ShallowValidate'방법을 재정의해야한다는 것을 알았습니다. 이 재정의에서 필요한 모델 유효성 검사기를 필터링합니다. 그리고 이제 IgnoreRequiredOrDefaultBodyModelValidator 클래스와 IgnoreRequiredValidations 속성 클래스 :

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Web.Http.Controllers;
using System.Web.Http.Metadata;
using System.Web.Http.Validation;

namespace your_namespace.Web.Http.Validation
{
    public class IgnoreRequiredOrDefaultBodyModelValidator : DefaultBodyModelValidator
    {
        private static ConcurrentDictionary<HttpActionBinding, bool> _ignoreRequiredValidationByActionBindingCache;

        static IgnoreRequiredOrDefaultBodyModelValidator()
        {
            _ignoreRequiredValidationByActionBindingCache = new ConcurrentDictionary<HttpActionBinding, bool>();
        }

        protected override bool ShallowValidate(ModelMetadata metadata, BodyModelValidatorContext validationContext, object container, IEnumerable<ModelValidator> validators)
        {
            var actionContext = validationContext.ActionContext;

            if (RequiredValidationsIsIgnored(actionContext.ActionDescriptor.ActionBinding))
                validators = validators.Where(v => !v.IsRequired);          

            return base.ShallowValidate(metadata, validationContext, container, validators);
        }

        #region RequiredValidationsIsIgnored
        private bool RequiredValidationsIsIgnored(HttpActionBinding actionBinding)
        {
            bool ignore;

            if (!_ignoreRequiredValidationByActionBindingCache.TryGetValue(actionBinding, out ignore))
                _ignoreRequiredValidationByActionBindingCache.TryAdd(actionBinding, ignore = RequiredValidationsIsIgnored(actionBinding.ActionDescriptor as ReflectedHttpActionDescriptor));

            return ignore;
        }

        private bool RequiredValidationsIsIgnored(ReflectedHttpActionDescriptor actionDescriptor)
        {
            if (actionDescriptor == null)
                return false;

            return actionDescriptor.MethodInfo.GetCustomAttribute<IgnoreRequiredValidationsAttribute>(false) != null;
        } 
        #endregion
    }

    [AttributeUsage(AttributeTargets.Method, Inherited = true)]
    public class IgnoreRequiredValidationsAttribute : Attribute
    {

    }
}

출처 :


If you don't want to use another ViewModel you can disable client validations on the view and also remove the validations on the server for those properties you want to ignore. Please check this answer for a deeper explanation https://stackoverflow.com/a/15248790/1128216


In my case the same Model was used in many pages for re-usability purposes. So what i did was i have created a custom attribute which checks for exclusions

public class ValidateAttribute : ActionFilterAttribute
{
    public string Exclude { get; set; }
    public string Base { get; set; }
    public override void OnActionExecuting(HttpActionContext actionContext)
    {
        if (!string.IsNullOrWhiteSpace(this.Exclude))
        {
            string[] excludes = this.Exclude.Split(',');
            foreach (var exclude in excludes)
            {
                actionContext.ModelState.Remove(Base + "." + exclude);
            }
        }
        if (actionContext.ModelState.IsValid == false)
        {
            var mediaType = new MediaTypeHeaderValue("application/json");
            var error = actionContext.ModelState;

            actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.OK, error.Keys, mediaType);

        }
    }
}

and in your controller

[Validate(Base= "person",Exclude ="Age,Name")]
    public async Task<IHttpActionResult> Save(User person)
    {

            //do something           

    }

Say the Model is

public class User
{
    public int Id { get; set; }
    [Required]
    public string Name { get; set; }
    [Range(18,99)]
    public string Age { get; set; }
    [MaxLength(250)]
    public string Address { get; set; }
}

Yes it is possible to disable Required Attribute. Create your own custom class attribute (sample code called ChangeableRequired) to extent from RequiredAtribute and add a Disabled Property and override the IsValid method to check if it is disbaled. Use reflection to set the disabled poperty, like so:

Custom Attribute:

namespace System.ComponentModel.DataAnnotations
{
    public class ChangeableRequired : RequiredAttribute
    {
       public bool Disabled { get; set; }

       public override bool IsValid(object value)
       {
          if (Disabled)
          {
            return true;
          }

          return base.IsValid(value);
       }
    }
}

Update you property to use your new custom Attribute:

 class Forex
 {
 ....
    [ChangeableRequired]
    public decimal? ExchangeRate {get;set;}
 ....
 }

where you need to disable the property use reflection to set it:

Forex forex = new Forex();
// Get Property Descriptor from instance with the Property name
PropertyDescriptor descriptor = TypeDescriptor.GetProperties(forex.GetType())["ExchangeRate"];
//Search for Attribute
ChangeableRequired attrib =  (ChangeableRequired)descriptor.Attributes[typeof(ChangeableRequired)];

// Set Attribute to true to Disable
attrib.Disabled = true;

This feels nice and clean?

NB: The validation above will be disabled while your object instance is alive\active...

참고URL : https://stackoverflow.com/questions/5367287/disable-required-validation-attribute-under-certain-circumstances

반응형