Search code examples
asp.net-mvcvalidationviewmodelmodelstate

Asp MVC 5 - ModelState is Invalid?


I have the following view model:

public class CreateCaseViewModel
{
    [Required]
    public string Subject { get; set; }

    [Required]
    [DisplayName("Post Content")]
    [UIHint("ForumEditor"), AllowHtml]
    [DataType(DataType.MultilineText)]
    public string PostContent { get; set; }

    // some other dropdown properties

}

The following controller action:

 [HttpPost]
 [ValidateAntiForgeryToken]
 [ValidateInput(false)]
 public ActionResult Create(CreateCaseViewModel viewModel, FormCollection collection)
 {

        // Re-populate dropdowns 
        viewModel.Categories = _unitOfWork.CategoryRepository.GetCategories();


        viewModel.Subject = collection["Subject"];
        viewModel.PostContent = collection["Description"];

        try
        {
            if (ModelState.IsValid)
            {
                // Do stuff
            }
        } 
        catch (DataException  dex  )
        {
           throw new ApplicationException("Something :", dex);
        }

        return View(viewModel);    

 }

I am manually assigning the value to PostContent from a value in FormCollection as you can see from code above. However I still keep getting modelstate is invalid - I'm returned back to the view with the validation error saying `The Post Content field is required'

Why is modelstate invalid?


Solution

  • When you submit the form the model binder will read the posted request data and map it to your method parameter. After that model validation framework will do the validation. It does not look at your FormCollection for doing this. So in your case, your model validation is failing because as per your view model it is expecting a value for PostContent property and it is not available there. Your action method code where you are setting the value of it gets executed later ( by this time model validation already occurred).

    Your options are, either standardize the input element name with your view model property name (rename the PostContent to Description or vice versa)

    public class CreateCaseViewModel
    {
        [Required]
        public string Subject { get; set; }
    
        [Required]
        [DisplayName("Post Content")]
        [UIHint("ForumEditor"), AllowHtml]
        [DataType(DataType.MultilineText)]
        public string Description { get; set; }    
    
    }
    

    Now let the model binder maps the request body to your view model parameter. Remove the manual assignment from the FormCollection in your action method

    Or you can probably create a new custom model binder which does the custom mapping for you (same as what you did in your action method).

    I would go with option one. Let the default model binder takes care of it.