Отключить обязательный атрибут проверки при определенных обстоятельствах - PullRequest
122 голосов
/ 20 марта 2011

Мне было интересно, возможно ли отключить обязательный атрибут проверки в определенных действиях контроллера.Мне интересно это, потому что в одной из моих форм редактирования я не требую, чтобы пользователь вводил значения для полей, которые они уже указали ранее.Однако затем я реализую логику, что, когда они вводят значение, он использует некоторую специальную логику для обновления модели, такую ​​как хеширование значения и т. Д.

Любые предположения о том, как обойти эту проблему?

РЕДАКТИРОВАТЬ:И да, здесь проблема с проверкой клиента, так как она не позволит им отправить форму без ввода значения.

Ответы [ 16 ]

73 голосов
/ 20 марта 2011

Эта проблема может быть легко решена с помощью моделей просмотра.Модели представлений - это классы, которые специально адаптированы к потребностям данного представления.Так, например, в вашем случае у вас могут быть следующие модели представлений:

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)
{
    ...
}
55 голосов
/ 20 марта 2012

Если вы просто хотите отключить проверку для отдельного поля на стороне клиента, вы можете переопределить атрибуты проверки следующим образом:

@Html.TexBoxFor(model => model.SomeValue, 
                new Dictionary<string, object> { { "data-val", false }})
39 голосов
/ 19 сентября 2013

Я знаю, что на этот вопрос уже давно дан ответ, и принятый ответ действительно сделает всю работу. Но есть одна вещь, которая беспокоит меня: необходимость копировать только две модели, чтобы отключить проверку.

Вот мое предложение:

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)
{
    ...
}
18 голосов
/ 09 апреля 2015

Вы можете удалить всю проверку свойства со следующим в вашем действии контроллера:

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

@ Ian's комментарий относительно MVC5

Следующее все еще возможно

ModelState.Remove("PropertyNameInModel");

Немного раздражает, что вы теряете статическую типизацию с обновленным API.Вы можете достичь чего-то похожего на старый способ, создав экземпляр помощника HTML и используя NameExtensions Methods .

14 голосов
/ 12 июня 2013

Клиентская сторона Чтобы отключить проверку формы, ниже приведены несколько вариантов, основанных на моих исследованиях. Надеюсь, один из них сработает для вас.

Вариант 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

14 голосов
/ 20 марта 2011

Лично я склонен использовать подход, который Дарин Димитров показал в своем решении.Это освобождает вас от возможности использовать подход аннотации данных с проверкой И иметь отдельные атрибуты данных в каждой модели представления, соответствующие выполняемой задаче.Чтобы свести к минимуму объем работы по копированию между моделью и моделью представления, вам следует обратиться к AutoMapper или ValueInjecter .У обоих есть свои сильные стороны, поэтому проверьте их оба.

Другим возможным подходом для вас будет получение вашей модели или модели представления из IValidatableObject.Это дает вам возможность реализовать функцию Validate.В валидации вы можете вернуть либо элементы List of ValidationResult, либо выдать возврат доходности для каждой проблемы, обнаруженной вами при валидации.

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;
}
7 голосов
/ 21 марта 2011

Самый простой способ - отключить проверку на стороне клиента, а на стороне сервера вам потребуется:

  1. ModelState ["SomeField"]. Errors.Clear (в вашем контроллереили создайте фильтр действий для удаления ошибок перед выполнением кода контроллера)
  2. Добавьте ModelState.AddModelError из кода контроллера при обнаружении нарушения обнаруженных проблем.

Кажется дажемодель пользовательского представления здесь не решит проблему, потому что число этих «предварительно отвеченных» полей может варьироваться.Если этого не произойдет, то пользовательская модель представления действительно может быть самым простым способом, но, используя описанную выше технику, вы можете обойти проблемы с проверками.

5 голосов
/ 10 апреля 2015

это был чей-то ответ в комментариях ... но это должен быть реальный ответ:

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

протестировано на MVC 6 с полем, имеющим атрибут [Required]

ответ украден от https://stackoverflow.com/users/73382/rob выше

2 голосов
/ 01 августа 2012

У меня возникла эта проблема при создании представления редактирования для моей модели, и я хочу обновить только одно поле.

Мое решение для простейшего способа состоит в том, чтобы поместить два поля, используя:

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

Комментарии - это поле, которое я обновляю только в представлении редактирования, которое не имеет обязательного атрибута.

ASP.NET MVC 3 Entity

1 голос
/ 30 июля 2018

Я искал решение, где я мог бы использовать ту же модель для вставки и обновления в веб-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
    {

    }
}

Источники:

...