ChatGPT解决这个技术问题 Extra ChatGPT

Disable Required validation attribute under certain circumstances

I was wondering if it is possible to disable the Required validation attribute in certain controller actions. I am wondering this because on one of my edit forms I do not require the user to enter values for fields that they have already specified previously. However I then implement logic that when they enter a value it uses some special logic to update the model, such as hashing a value etc.

Any sugestions on how to get around this problem?

EDIT: And yes client validation is a problem here to, as it will not allow them to submit the form without entering a value.

+1 good Q. Would be good to mention client validation here. One option is to remove the RequiredAttr completely and do a server side check when you need to. But this would be tricky on the client
Points for anyone who also covers disabling certain fields from client validation (no removing the references to jquery validation)
Maybe i'm missing your point, but if the user has already specified the values beforehand, then those values are already present, and thus will pass Required validation. Did you mean something else?
Because these values have since been hashed, such as password and security answer, so if they enter a new value on the edit form I want to re-hash the new value before insertion, but i also want the option for it to be left blank sort of thing.
@gideon: see Adrian Smith's answer: stackoverflow.com/a/9781066/114029

D
Darin Dimitrov

This problem can be easily solved by using view models. View models are classes that are specifically tailored to the needs of a given view. So for example in your case you could have the following view models:

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

    ... some other properties
}

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

    ... some other properties
}

which will be used in their corresponding controller actions:

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

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

Not always true if you have a boolean/bit that is not nullable. But really doesn't make a difference since its going to end up true or false. I had css to highlight required fields and it falsely highlighted the CheckBoxFor item. My solution: $("#IsValueTrue").removeAttr("data-val-required");
In update we generally have (FormCollection collection). could you please explain how you are using model as parameter
No, we don't usually have Update(FormCollection collection), at least I never do. I always define and use a specific view model: Update(UpdateViewView model).
Overloading methods with the same HTTP action are not allowed, so to me it seems like this would not work. Am I missing something?
@edgi, no, you are not missing anything. It's a mistake in my post. The second action method should obviously be called Insert. Thanks for pointing this out.
A
Andrew

If you just want to disable validation for a single field in client side then you can override the validation attributes as follows:

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

What worked for me via jQuery: $("#SomeValue").removeAttr("data-val-required");
I like this approach but I needed to re-parse the form validation attributes using: $('form').removeData('unobtrusiveValidation'); $('form').removeData('validator'); $.validator.unobtrusive.parse('selector for your form');
@Html.TexBoxFor(model => model.SomeValue, new { data_val = false }) - easier to read IMO.
I liked most of this approach to leave me give a fine control of each field, but you could add to cancel ModelState.IsValid when send data to save by POST. Maybe cause some risk ?
If you want to disable it through jQuery: $(".search select").attr('data-val', false);
P
PhilDulac

I know this question has been answered a long time ago and the accepted answer will actually do the work. But there's one thing that bothers me: having to copy 2 models only to disable a validation.

Here's my suggestion:

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

This way, you don't have to bother with client/server side validations, the framework will behave the way it's supposed to. Also, if you define a [Display] attribute on the base class, you don't have to redefine it in your UpdateModel.

And you can still use these classes the same way:

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

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

I like this approach better, especially if you have a long list of validation attributes. In your example, you can add more attributes in the base class to have the benefit more apparent. The only disadvantage I can see is that there is no way for inheriting properties to override the attributes. Example, if you have a [Required] property on the base class, the inheriting property is forced to be [Required] also, unless you have a custom [Optional] attribute.
I thought of something like this too, although I have a viewModel that has an object 'Project' that has multiple attributes and i only want one of these attributes validated under certain circumstances. I dont think i can just override a attribute of an object right? any advice?
You can't override the attribute. The base class should only contain common attributes for all sub classes. Then, your sub classes should define the attributes they need.
Most elegant, reusable, clear solution. Replications are bad. Polymorphism is the way. +1
in my case a base class has a required attribute and I want to make it non required in my parent class. Is it possible without having two copies of model?
C
Community

You can remove all validation off a property with the following in your controller action.

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

@Ian's comment regarding MVC5

The following is still possible

ModelState.Remove("PropertyNameInModel");

Bit annoying that you lose the static typing with the updated API. You could achieve something similar to the old way by creating an instance of HTML helper and using NameExtensions Methods.


Except... there isn't a method on ModelState that matches that signature. Not in MVC 5, at least.
The question wasn't how to remove all validation. It was how to remove required field validation. You might want to do this while leaving other validation rules in place.
This is indeed the best solution and what worked for me even in .net core thank you @jps ModelState.Remove("PropertyNameInModel");
C
Community

Client side For disabling validation for a form, multiple options based on my research is given below. One of them would would hopefully work for you.

Option 1

I prefer this, and this works perfectly for me.

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

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

and invoking it like

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

Option 2

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

Option 3

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

Option 4

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

Option 5

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

Server Side

Create an attribute and mark your action method with that attribute. Customize this to adapt to your specific needs.

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

A better approach has been described here Enable/Disable mvc server side validation dynamically


$('input selector').each(function () { $(this).rules('remove');}); helped me
The question was specifically about removing required field validation, not about removing all validation. Your answer is about removing all validation.
m
mindplay.dk

Personally I would tend to use the approach Darin Dimitrov showed in his solution. This frees you up to be able to use the data annotation approach with validation AND have separate data attributes on each ViewModel corresponding to the task at hand. To minimize the amount of work for copying between model and viewmodel you should look at AutoMapper or ValueInjecter. Both have their individual strong points, so check them both.

Another possible approach for you would be to derive your viewmodel or model from IValidatableObject. This gives you the option to implement a function Validate. In validate you can return either a List of ValidationResult elements or issue a yield return for each problem you detect in validation.

The ValidationResult consists of an error message and a list of strings with the fieldnames. The error messages will be shown at a location near the input field(s).

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

can we hook this on client side?
client-side validation generally is done for individual fields only, performing interactive, field-by-field validation feedback, as a convenience to the user. Since the object-validation step generally involves dependent validation (multiple fields and/or conditions that are external to the object itself) this can't necessarily be performed on the client-side, even if you could compile the code to JavaScript. If complex/dependent validation on the client-side does add value in your case, you will need to use the onsubmit-callback and duplicate the validation logic on the client-side.
A
Adam Tuliper

The cleanest way here I believe is going to disable your client side validation and on the server side you will need to:

ModelState["SomeField"].Errors.Clear (in your controller or create an action filter to remove errors before the controller code is executed) Add ModelState.AddModelError from your controller code when you detect a violation of your detected issues.

Seems even a custom view model here wont solve the problem because the number of those 'pre answered' fields could vary. If they dont then a custom view model may indeed be the easiest way, but using the above technique you can get around your validations issues.


This is just what I needed. I had one view and differente actions occur in this view, so I couldn't make it with differente ViewModels. This works like a charm
C
Community

this was someone else's answer in the comments...but it should be a real answer:

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

tested on MVC 6 with a field having the [Required] attribute

answer stolen from https://stackoverflow.com/users/73382/rob above


What about server side validation?
ModelState.Remove, right? At any rate, for me, the issue was that I had a second entity being included in my model...the primary I wanted validation on, but the secondary didn't need validation on that page...so, under this scenario, only the JQuery was necessary.
I think that the link is broken, so please edit. This is a partial answer
It's weird that you say this works in MVC6 (I don't have option to test on MVC6 currently) but does not work for me on MVC4 which I am currently using.
The question is for MVC/C# - not JS, the answer would not work server side
F
Felipe FMMobile

I was having this problem when I creating a Edit View for my Model and I want to update just one field.

My solution for a simplest way is put the two field using :

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

Comments is the field that I only update in Edit View, that not have Required Attribute.

ASP.NET MVC 3 Entity


C
Community

AFAIK you can not remove attribute at runtime, but only change their values (ie: readonly true/false) look here for something similar . As another way of doing what you want without messing with attributes I will go with a ViewModel for your specific action so you can insert all the logic without breaking the logic needed by other controllers. If you try to obtain some sort of wizard (a multi steps form) you can instead serialize the already compiled fields and with TempData bring them along your steps. (for help in serialize deserialize you can use MVC futures)


N
Nick Albrecht

What @Darin said is what I would recommend as well. However I would add to it (and in response to one of the comments) that you can in fact also use this method for primitive types like bit, bool, even structures like Guid by simply making them nullable. Once you do this, the Required attribute functions as expected.

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
}

Y
Yom T.

As of MVC 5 this can be easily achieved by adding this in your global.asax.

DataAnnotationsModelValidatorProvider.AddImplicitRequiredAttributeForValueTypes = false;

R
Roberto B

I was looking for a solution where I can use the same model for an insert and update in web api. In my situation is this always a body content. The [Requiered] attributes must be skipped if it is an update method. In my solution, you place an attribute [IgnoreRequiredValidations] above the method. This is as follows:

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

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

    ...

What else needs to be done? An own BodyModelValidator must becreated and added at the startup. This is in the HttpConfiguration and looks like this: 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());
        }

        ...

My own BodyModelValidator is derived from the DefaultBodyModelValidator. And i figure out that i had to override the 'ShallowValidate' methode. In this override i filter the requierd model validators. And now the IgnoreRequiredOrDefaultBodyModelValidator class and the IgnoreRequiredValidations attributte class:

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
    {

    }
}

Sources:

Using string debug = new StackTrace().ToString() to find out who is handeling the model validation.

https://docs.microsoft.com/en-us/aspnet/web-api/overview/advanced/configuring-aspnet-web-api to know how set my own validator.

https://github.com/ASP-NET-MVC/aspnetwebstack/blob/master/src/System.Web.Http/Validation/DefaultBodyModelValidator.cs to figure out what this validator is doing.

https://github.com/Microsoft/referencesource/blob/master/System.Web/ModelBinding/DataAnnotationsModelValidator.cs to figure out why the IsRequired property is set on true. Here you can also find the original Attribute as a property.


C
Community

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


B
Beingnin

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

j
jmoerdyk

This one worked for me:

$('#fieldId').rules('remove', 'required');

E
Ernest Gunning

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...


After a few debates on this I would like to mention that I don't recommend disabling validation but rather review the rule. If you disable it, you creating a dependency to re-enable the rule and I would suggest review the rule.
Changing a static value to completely disable all validation of that field executed within that same process sounds like a terrible idea.

关注公众号,不定期副业成功案例分享
Follow WeChat

Success story sharing

Want to stay one step ahead of the latest teleworks?

Subscribe Now