Отключить Обязательный атрибут проверки при определенных обстоятельствах

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

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

EDIT:
И да, проверка клиента является проблемой здесь, поскольку она не позволит им отправить форму без ввода значения.

Ответ 1

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

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)
{
    ...
}

Ответ 2

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

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

Ответ 3

Я знаю, что на этот вопрос был дан ответ давным-давно, и принятый ответ действительно выполнит эту работу. Но меня беспокоит одна вещь: копировать 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)
{
    ...
}

Ответ 4

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

Другим возможным подходом для вас было бы получение вашей модели представления или модели из документа IValidatableObject. Это дает вам возможность реализовать функцию Validate. В validate вы можете возвращать элементы списка 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;
}

Ответ 5

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

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

Ответ 6

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

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

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

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

ModelState.Remove("PropertyNameInModel");

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

Ответ 7

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

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

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

Ответ 8

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

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

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

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

Ответ 9

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

Мое решение для простейшего способа ставит два поля:

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

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

ASP.NET MVC 3 Entity

Ответ 10

AFAIK вы не можете удалить атрибут во время выполнения, но только измените их значения (то есть: readonly true/false) посмотрите здесь что-то подобное. В качестве другого способа сделать то, что вы хотите, не вникая в атрибуты, я пойду с ViewModel для вашего конкретного действия, чтобы вы могли вставить всю логику, не нарушая логику, требуемую другими контроллерами. Если вы попытаетесь получить какой-то мастер (многоступенчатая форма), вы можете вместо этого сериализовать уже скомпилированные поля, а с помощью TempData вывести их по шагам. (для помощи в сериализации десериализации вы можете использовать фьючерсы MVC)

Ответ 11

Что сказал @Darin - это то, что я бы рекомендовал. Однако я бы добавил к нему (и в ответ на один из комментариев), что на самом деле вы также можете использовать этот метод для примитивных типов, таких как бит, bool, даже структуры типа Guid, просто делая их обнуляемыми. Как только вы это сделаете, атрибут 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
}

Ответ 12

Как и в случае с MVC 5, это можно легко достичь, добавив это в свой global.asax.

DataAnnotationsModelValidatorProvider.AddImplicitRequiredAttributeForValueTypes = false;

Ответ 13

Если вы не хотите использовать другой ViewModel, вы можете отключить проверку клиента в представлении, а также удалить проверки на сервере для тех свойств, которые вы хотите игнорировать. Пожалуйста, проверьте этот ответ для более глубокого объяснения fooobar.com/questions/60284/...

Ответ 14

Да, можно отключить Обязательный атрибут. Создайте свой собственный атрибут класса (образец кода под названием ChangeableRequired) до степени от RequiredAtribute и добавьте свойство Disabled и переопределите метод IsValid, чтобы проверить, не разнесено ли оно. Используйте отражение, чтобы установить отключенную poperty, например:

Пользовательский атрибут:

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

Обновите свое свойство, чтобы использовать новый настраиваемый атрибут:

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

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

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;

Это приятно и чисто?

Примечание: проверка правильности выше будет отключена, пока экземпляр объекта активен\active...