How do I prevent hidden fields from interfering with server side validation in MVC?

3.1k Views Asked by At

I have a partial view that displays a number of inputs based on a view model. In some situations, some of those inputs are not rendered by the partial view, but are still decorated with [Required] attributes in the view model. As a result, when the form is posted back to my controller, ModelState.IsValid returns false. Is there a way to bypass this?

4

There are 4 best solutions below

0
On

I'd recommend separating your validation from your base model.

public class MyModel
{
  public string MyString { get; set; }
  public string MyHiddenField { get; set; }
}

public interface IMyModel_ValidateMystringOnly
{
  [Required]
  string MyString { get; set; }
}

[MetadataType(TypeOf(IMyModel_ValidateMystringOnly))]
public class MyModel_ValidateMystringOnly : MyModel

This allows you to create any number of validation types, and only validate what you want when you want.

public ActionResult ShowMyModel()
{
  var model = new MyModel(); // or Respository.GetMyModel() whatever..

  View(model);
}

public ActionResult ValidateModel(MyModel_ValidateMystringOnly model)
{
  if (ModelState.IsValid)
  {
    // Hey Validation!
  }

  // MyModel_ValidateMyStringOnly is a MyModel 
  // so it can be passed to the same view!
  return View("ShowMyModel", model);
}

This is just an example, but should be clear on how-to reuse the same model with or without validation.

0
On

You can use Foolproof to validate your fields conditionally. This way, they'll be required only when they need to, as you can see in the example of the link.

private class Person
{
    [Required]
    public string FirstName { get; set; }

    [Required]
    public string LastName { get; set; }

    public bool Married { get; set; }

    [RequiredIfTrue("Married")]
    public string MaidenName { get; set; }
}

In this example, MaidenName will only change your ModelState.IsValid to false if Married == true

0
On

I have used method at times where the form changes slightly based on specific DropDown or Radio Button selections.

Inside your Action method before you check ModelState.IsValid you can do something like ModelState.Remove("Object.PropertyName")

Note: The property name should be the same as the ID rendered to the client. Use a "." for any underscores.

    If isSomeCondition Then
        ModelState.Remove("Property1")
        ModelState.Remove("Property2")
    End If

    If ModelState.IsValid() Then
       ...
    End If
1
On

You should always separate your VIEW model from your DOMAIN model. There is a very good reason for this and it has to do with security. When you use your domain models as your view models you are vulnerable to an overposting and/or underposting attacks. You can read more about it on these pages:

  1. http://odetocode.com/blogs/scott/archive/2012/03/12/complete-guide-to-mass-assignment-in-asp-net-mvc.aspx
  2. http://blogs.msdn.com/b/rickandy/archive/2012/03/23/securing-your-asp-net-mvc-4-app-and-the-new-allowanonymous-attribute.aspx
  3. https://hendryluk.wordpress.com/tag/asp-net-mvc/

In short if you don't need a field then it should not be in your view model. You should convert - map your view models to domain models. Although it can be tedious it makes your application much more secure. There are libraries you can use to help you with mapping such as Automapper.

EDIT: Since my original answer, I have come to a conclusion that the easiest way to deal with this type of scenario is to have your view model implement IValidatableObject interface and then write your validation logic inside the Validate method. It does not give you client side validation but it is the most effective and clean way to accomplish custom/scenario based validation without writing your own custom filters.

You can read more about it here: http://weblogs.asp.net/scottgu/class-level-model-validation-with-ef-code-first-and-asp-net-mvc-3