Disable Model Validation in Asp.Net MVC
Asked Answered
F

7

10

How do I disable Model validation for a single Action in a Controller ? Or can I do it per model by registering the model type at startup somewhere ?

I want the ModelBinder to bind to the model, but afterwards it should not perform the model validation.

The reason why i dont want validation to happen is because i am trying to move the logic from the controllers to a service layer which will be responsible for validating the models as i cant assume that models being passed to a service contains valid data.

As I understand this is the recommend approach (to not have logic in the controllers), so I find it a bit strange that i cant seem to find anything about how the model validation can be disabled (per action or per model type).

Please notice that I dont want to disable model validation for the entire webapplication (by removing the validation providers), and i dont want to disable the input validation that checks for malicious code being posted.


UPDATE

I am using .Net 4.0 and MVC 3 Preview 1

Flavio answered 26/8, 2010 at 19:30 Comment(0)
F
3

Unfortunately there seems to be no easy way to disable the model validation happening in the ModelBinder except for registering every single model type you don’t want to validate (including nested complex types) with a specific ModelBinder. It can be done with the following code:

ModelBinders.Binders[typeof(MyModelType)] = new NonValidatingModelBinder();

Creating a SkipValidationAttribute that can be attached to action methods or action method parameters doesn’t seem possible as it should be implemented in the ControllerActionInvoker, but there is no way of letting the ModelBinder know that it should do any validation in the SetProperty() and OnModelUpdated methods when calling BindModel() in the GetParameterValue() method.

Flavio answered 30/8, 2010 at 0:41 Comment(2)
I made the " SkipValidationAttribute" approach work, by creating my own impl of ControllerActionInvoker which gets the attribute from the method parameter and if it is declared it adds a key to the modelstate injected into the ModelBinder (BindModel method), and remove it again when the model is bound. Then i can check in the ModelBinder if the modelstate contains the key and if it does it skips validation, if not just performs validation like it normally would.Flavio
@Kugel: Even though way to late, NonValidatatingModelBinder you have to implement yourself.Flavio
V
19

Just remove the items you don´t need before checking if the model is valid

ModelState.Remove("Email");
if (ModelState.IsValid)
{
   // your logic
}
Vastitude answered 5/6, 2012 at 11:18 Comment(2)
This one is more faster & easy!Shan
This is short, clean and does help.Levileviable
D
11

I've solved this problem with this code:

public ActionResult Totals(MyModel model)
{
    ModelState.Clear();
    return View(model);
}

Not sure it's the right way though.

Decalcomania answered 20/11, 2012 at 15:1 Comment(1)
Model state data does more than hold validation. By clearing the model state you will lose other functionality as well.Targett
F
3

Unfortunately there seems to be no easy way to disable the model validation happening in the ModelBinder except for registering every single model type you don’t want to validate (including nested complex types) with a specific ModelBinder. It can be done with the following code:

ModelBinders.Binders[typeof(MyModelType)] = new NonValidatingModelBinder();

Creating a SkipValidationAttribute that can be attached to action methods or action method parameters doesn’t seem possible as it should be implemented in the ControllerActionInvoker, but there is no way of letting the ModelBinder know that it should do any validation in the SetProperty() and OnModelUpdated methods when calling BindModel() in the GetParameterValue() method.

Flavio answered 30/8, 2010 at 0:41 Comment(2)
I made the " SkipValidationAttribute" approach work, by creating my own impl of ControllerActionInvoker which gets the attribute from the method parameter and if it is declared it adds a key to the modelstate injected into the ModelBinder (BindModel method), and remove it again when the model is bound. Then i can check in the ModelBinder if the modelstate contains the key and if it does it skips validation, if not just performs validation like it normally would.Flavio
@Kugel: Even though way to late, NonValidatatingModelBinder you have to implement yourself.Flavio
I
3

I definitely dislike this addition in the 2.0 version, because, as you stated in your question, validation makes more sense in the Service layer. In this way you can reuse it in other non-web applications, and test it more easily without having to mock the auto-validation mechanism.

Validation in Controller layer is pointless because in this part you can only verify model data and not business rules. For example, think of a service responsible of adding new comments and a user that wants to post a new one, the data in the comment he/she is posting may be valid, but what happens if the user is banned to comment because of misbehavior in the past? You should do some validation in the Service layer to ensure this is not happening, and if it does, throwing an exception. In short, validation must be done in the Service layer.

I use xVal as my Validation framework because it's compatible with the DataAnnotationModel, allows me to place validation wherever I want and performs client-side validation without extra-effort, even remote-client side. This is how I use it at the beginning of each of my services, for example, a login service:

public void SignIn(Login login) {
    var loginErrors = DataAnnotationsValidationRunner.GetErrors(login);

    // Model validation: Empty fields?
    if (loginErrors.Any())
        throw new RulesException(loginErrors);

    // Business validation: Does the user exist? Is the password correct?
    var user = this._userRepository.GetUserByEmail(login.Email);

    if (user == null || user.Password != login.Password)

        throw new RulesException(null, "Username or password invalids");

    // Other login stuff...
}

Simple, web-independent and easy... then, in the Controller:

public RedirectResult Login(Login login) {

    // Login the user

    try {

        this._authenticationRepository.SignIn(login);

    } catch (RulesException e) {

        e.AddModelStateErrors(base.ModelState, "Login");

    }

    // Redirect

    if (base.ModelState.IsValid)
        return base.Redirect(base.Url.Action("Home"));
    else return base.Redirect(base.Url.Action("Login"));
}
Imphal answered 25/9, 2010 at 22:43 Comment(0)
F
2

I would recommend you perform validation in both places rather than trying to turn off validation in the UI. I understand your point that the service cannot assume that it's being passed valid data - that is a valid concern and is the reason your service should have validation. But you should also have validation in your UI. This is also nice because you can have client-side validation to prevent user errors and give you users a better experience.

Fireside answered 26/8, 2010 at 21:26 Comment(9)
Thanks for your reply. I have clientside UI validation. I use the attributes in the DataAnnotations namespace for my models. My service layer adds the validation errors to the Modelstate (if in a web context). There is no reason to perform the same validation twice. :)Flavio
Basically my service layer follows this approach asp.net/mvc/tutorials/validating-with-a-service-layer--cs It works fine, but the example doesnt use the DataAnnotations attributes for validation, which are automatically validated in MVC (and what i am trying to prevent).Flavio
That link doesn't have a date as to when it was written but my guess is that it's pretty old. When MVC was first released, there was some confusion around where validation logic should go (controller, etc.). But that story has been solidified in the last year or so with the recommended best practice typically being that UI validation should happen in the model binder which is why that's now the default in MVC 2.Fireside
If you are set on doing it this way, then it could be accomplish by inheriting the DataAnnotationModelBinder and then override the OnModelUpdated() method and inside just do ModelState.Clear(). I'm not saying I would do this though. :)Fireside
I assume you are talking about the DefaultModelBinder (as there is no DataAnnotationsModelBinder - there is a DataAnnotationsModelValidator though). Doing a ModelState.Clear() inside the ModelBinder will affect the entire WebApplication which i dont want to, i want it to keep working the traditional way, but be able to disable the model validation either per action or per model type. I could of course make a big switch or if clause and add every single Model type that i dont want validated - but that would be pretty ugly i guess - but maybe it is the best option! :)Flavio
I can see that the OnModelUpdated method gets a ControllerContext and a ModelBindingContext. The best option would be if it is somehow possible to figure out which Action is being called and then check if it has a Custom attribute (ValidateModel etc) attached to it to prevent it from validating the model. Another option is to register a ModelBinder that doesnt do the validation, and register every single type - something like ModelBinders.Binders[typeof(MyModelType)] = new NonValidatingModelBinder(); Thanks for replying. There is a couple of options that i can try to work on. :)Flavio
Yes, I meant to say "DefaultModelBinder". I would use your second approach: ModelBinders.Binders[typeof(MyModelType)] = new NonValidatingModelBinder();. That way it doesn't affect your application globally.Fireside
Just to give a small update (if it has any interest to you), it is possible to attach attributes to parameters (like the BindAttribute), so it should be easy to check if the parameter/model have an attribute attached in the OnModelUpdated and just disable the validation if it have. So when i dont need model validation i can just disable it by doing something like public ActionResult ActionMethodName ([DisableModelValidation] MyModelType model) {...} :)Flavio
As a usability specialist I disagree. Users hate not getting all the validations at once. Unless you duplicate them all splitting them is bad form.Targett
T
1

I know that this already been answered but what you really needed was to extend the DataAnnotationsValidatorProvider and override the GetValidators method.

Then, on startup, you would remove the DataAnnotationsValidatorProvider from ModelValidatorProviders.Providers and add your new one.

Needless to say, if you simply don't want any validation at all, you can simply have the GetValidators method returning an empty collection.

In my case, I need to remove validation only when submitting the forms while still keeping the client-side validation, hence the following:

public class DynamicModelValidatorProvider : DataAnnotationsModelValidatorProvider
{
   GetValidators(ModelMetadata metadata, ControllerContext context, IEnumerable<Attribute> attributes)
    {
        if (context.HttpContext.Request.HttpMethod == "POST")
        {
            return new ModelValidator[] { };
        }

        return base.GetValidators(metadata, context, attributes);
    }
}
Thrill answered 21/10, 2016 at 17:53 Comment(0)
M
-4

I use [ ValidateInput( false )]

Not sure if this prevents model validation, or only IIS submit validation.

Mildred answered 26/8, 2010 at 20:39 Comment(1)
It doesnt prevent the model from being validated. As far as i know it is used to disable the check for malicious input (like html tags or javascript etc.)Flavio

© 2022 - 2024 — McMap. All rights reserved.