Deshabilitar el atributo de validación requerido bajo ciertas circunstancias


Me preguntaba si es posible deshabilitar el atributo de validación requerido en ciertas acciones del controlador. Me pregunto esto porque en uno de mis formularios de edición no requiero que el usuario ingrese valores para los campos que ya han especificado anteriormente. Sin embargo, luego implemento la lógica que cuando ingresan un valor utiliza alguna lógica especial para actualizar el modelo, como hash un valor, etc.

¿Alguna sugerencia sobre cómo solucionar este problema?

EDITAR:
Y sí la validación del cliente es un problema aquí, ya que no les permitirá enviar el formulario sin ingresar un valor.

Author: H.B., 2011-03-20

15 answers

Este problema se puede resolver fácilmente usando modelos de vista. Los modelos de vista son clases que se adaptan específicamente a las necesidades de una vista determinada. Así, por ejemplo, en su caso podría tener los siguientes modelos de vista:

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

    ... some other properties
}

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

    ... some other properties
}

Que se utilizarán en sus correspondientes acciones de controlador:

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

[HttpPost]
public ActionResult Insert(InsertViewModel model)
{
    ...
}
 69
Author: Darin Dimitrov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-12-04 06:42:10

Si solo desea deshabilitar la validación para un solo campo en el lado del cliente, puede anular los atributos de validación de la siguiente manera:

@Html.TexBoxFor(model => model.SomeValue, 
                new Dictionary<string, object> { { "data-val", false }})
 50
Author: Adrian Smith,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-03-20 03:42:45

Sé que esta pregunta ha sido respondida hace mucho tiempo y la respuesta aceptada realmente hará el trabajo. Pero hay una cosa que me molesta: tener que copiar 2 modelos solo para desactivar una validación.

Aquí está mi sugerencia:

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

De esta manera, no tiene que molestarse con las validaciones del lado cliente/servidor, el marco se comportará de la manera que se supone que debe. Además, si define un atributo [Display] en la clase base, no tiene que redefinirlo en su UpdateModel.

Y todavía puedes usar estas clases de la misma manera:

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

[HttpPost]
public ActionResult Insert(InsertModel model)
{
    ...
}
 35
Author: PhilDulac,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-09-19 14:56:09

Personalmente tendería a utilizar el enfoque que Darin Dimitrov mostró en su solución. Esto le libera para poder usar el enfoque de anotación de datos con validación Y tener atributos de datos separados en cada ViewModel correspondientes a la tarea en cuestión. Para minimizar la cantidad de trabajo para copiar entre el modelo y viewmodel, debe mirar AutoMapper o ValueInjecter. Ambos tienen sus puntos fuertes individuales, así que revísalos a ambos.

Otro enfoque posible para usted sería derivar tu viewmodel o modelo de IValidatableObject. Esto le da la opción de implementar una función Validate. En validar puede devolver una lista de elementos ValidationResult o emitir un retorno de rendimiento para cada problema que detecte en la validación.

El ValidationResult consiste en un mensaje de error y una lista de cadenas con los nombres de campo. Los mensajes de error se mostrarán en una ubicación cerca de los campos de entrada.

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;
}
 14
Author: nttakr,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-03-29 15:16:58

Lado del cliente Para deshabilitar la validación de un formulario, a continuación se ofrecen varias opciones basadas en mi investigación. Uno de ellos con suerte trabajaría para ti.

Opción 1

Prefiero esto, y esto funciona perfectamente para mí.

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

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

E invocarlo como

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

Opción 2

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

Opción 3

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

Opción 4

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

Opción 5

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

Lado del servidor

Crea un atributo y marca tu acción método con ese atributo. Personaliza esto para adaptarlo a tus necesidades específicas.

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

Aquí se ha descrito un mejor enfoque Habilitar / Deshabilitar la validación del lado del servidor mvc dinámicamente

 14
Author: int-i,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 11:55:07

Puede eliminar toda la validación de una propiedad con lo siguiente en la acción del controlador.

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

@Comentario de Ian sobre MVC5

Lo siguiente es todavía posible

ModelState.Remove("PropertyNameInModel");

Poco molesto que se pierde la escritura estática con la API actualizada. Podría lograr algo similar a la forma antigua creando una instancia de HTML helper y usando NameExtensions Methods.

 13
Author: jps,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 11:47:17

La forma más limpia aquí creo que va a desactivar la validación del lado del cliente y en el lado del servidor necesitará:

  1. ModelState ["someField"].Error.Clear (en su controlador o cree un filtro de acción para eliminar errores antes de que se ejecute el código del controlador)
  2. Añadir ModelState.AddModelError del código del controlador cuando detecte una violación de los problemas detectados.

Parece que incluso un modelo de vista personalizado aquí no resolverá el problema porque el el número de esos campos' pre contestados ' podría variar. Si no lo hacen, un modelo de vista personalizado puede ser la forma más fácil, pero utilizando la técnica anterior puede evitar sus problemas de validación.

 6
Author: Adam Tuliper - MSFT,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-03-21 05:06:25

Esta fue la respuesta de otra persona en los comentarios...pero debería ser una respuesta real:

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

Probado en MVC 6 con un campo que tiene el atributo [Required]

Respuesta robada de https://stackoverflow.com/users/73382/rob anterior

 5
Author: Mike_Matthews_II,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 11:47:17

Tuve este problema cuando creé una Vista de Edición para mi Modelo y quiero actualizar solo un campo.

Mi solución para una forma más simple es poner el campo dos usando:

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

Comentarios es el campo que solo actualizo en la vista de edición, que no tiene Atributo Obligatorio.

ASP.NET MVC 3 Entity

 2
Author: Felipe FMMobile,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-08-01 15:39:01

AFAIK no se puede eliminar el atributo en tiempo de ejecución, pero solo cambiar sus valores (es decir: readonly true/false) busque aquí algo similar. Como otra forma de hacer lo que quieras sin jugar con los atributos, iré con un ViewModel para tu acción específica para que puedas insertar toda la lógica sin romper la lógica necesaria por otros controladores. Si intenta obtener algún tipo de asistente (un formulario de pasos múltiples), puede serializar los campos ya compilados y con TempData traer a lo largo de sus pasos. (para ayuda en serialize deserialize puede usar MVC futures)

 1
Author: Iridio,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:34:38

Lo que @Darin dijo es lo que yo recomendaría también. Sin embargo, añadiría (y en respuesta a uno de los comentarios) que, de hecho, también puede usar este método para tipos primitivos como bit, bool, incluso estructuras como Guid simplemente haciéndolas nullables. Una vez hecho esto, el atributo Required funciona como se espera.

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
}
 1
Author: Nick Albrecht,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-06-12 14:40:21

A partir de MVC 5 esto se puede lograr fácilmente agregando esto en su global.asax.

DataAnnotationsModelValidatorProvider.AddImplicitRequiredAttributeForValueTypes = false;
 1
Author: jom,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-01-08 11:25:55

Estaba buscando una solución donde pueda usar el mismo modelo para una inserción y actualización en web api. En mi situación es esto siempre un contenido corporal. Los atributos [Requiered] deben omitirse si se trata de un método update. En mi solución, colocas un atributo [IgnoreRequiredValidations] encima del método. Esto es como sigue:

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

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

    ...

¿Qué más hay que hacer? Un BodyModelValidator propio debe ser creado y añadido al inicio. Esto está en HttpConfiguration y se ve así: 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());
        }

        ...

Mi propio BodyModelValidator se deriva del DefaultBodyModelValidator. Y me di cuenta de que tenía que anular el método' ShallowValidate'. En este override filtro los validadores del modelo requierd. Y ahora la clase IgnoreRequiredOrDefaultBodyModelValidator y la clase IgnoreRequiredValidations attributte:

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
    {

    }
}

Fuentes:

 1
Author: Roberto B,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-08-06 08:08:34

Si no desea utilizar otro ViewModel, puede deshabilitar las validaciones de cliente en la vista y también eliminar las validaciones en el servidor para aquellas propiedades que desea ignorar. Por favor revise esta respuesta para una explicación más profunda https://stackoverflow.com/a/15248790/1128216

 0
Author: Jonathan Morales Vélez,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:34:38

Sí es posible deshabilitar el atributo Requerido. Cree su propio atributo de clase personalizado (código de ejemplo llamado ChangeableRequired) en extensión desde RequiredAtribute y agregue una propiedad Disabled y anule el método isValid para verificar si está disbaled. Use reflexión para establecer el poperty deshabilitado, así:

Atributo personalizado:

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

Actualice su propiedad para usar su nuevo atributo personalizado:

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

Donde necesita deshabilitar la propiedad 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;

Esto se siente bien y limpio?

NB: La validación anterior se deshabilitará mientras su instancia de objeto esté viva\activa...

 0
Author: Ernest Gunning,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-03-15 13:59:13