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


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

любой sugestions о том, как обойти это проблема?

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

15 103

15 ответов:

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

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

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

еще один возможный подход для вас будет заключаться в том, чтобы получить ваш viewmodel или модель из 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;
}

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

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 динамически

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

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

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

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

ModelState.Remove("PropertyNameInModel");

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

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

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

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

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

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

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

ответ украден из 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 Entity

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

то, что сказал @Darin, я бы тоже рекомендовал. Однако я бы добавил к нему (и в ответ на один из комментариев), что вы можете также использовать этот метод для примитивных типов, таких как bit, bool, даже структуры, такие как Guid, просто сделав их nullable. После этого 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". В этом переопределении я фильтрую валидаторы модели requierd. А теперь класс IgnoreRequiredOrDefaultBodyModelvalidator и класс attributte 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
    {

    }
}

источники:

Если вы не хотите использовать другой ViewModel, вы можете отключить проверки клиента в представлении, а также удалить проверки на сервере для тех свойств, которые вы хотите игнорировать. Пожалуйста, проверьте этот ответ для более глубокого объяснения https://stackoverflow.com/a/15248790/1128216

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

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

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;

это чувствует себя хорошо и чисто?

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