Search code examples
c#asp.net-mvcasp.net-mvc-2-validation

ASPNET MVC - Why is ModelState.IsValid false "The x field is required" when that field does have a value?


I have a model like this:

public PurchaseOrder 
{
    [Required] [StringLength(15)]
    public virtual string OrderNumber {get;set;}
    // etc.        
}

When I submit an order from the view (using $.post, not input type=submit) it goes to my controller class:

public class PurchaseOrderController
{
    public JsonResult Save(PurchaseOrder order)
    {
        // TryUpdateModel(order); // commented out since modelstate.isvalid remains false anyway
        if (ModelState.IsValid)
        {
            // its never valid 
        }
    }
}

ModelState.IsValid always returns false, with the error: "The Order Number field is required." But there is a value in this field (?? why)

Why would it say "value is required" when it does have a value? Have I missed something? Is it because of the $.post instead of the submit? What can I do?

This is what the debugger looks like:

alt text http://www.freeimagehosting.net/uploads/f734f3d95d.png

EDIT: Extra info....

I really think that for some reason the model binding is not happening. When I try this code found here: )

if (!ModelState.IsValid)
{
    ModelState.Clear();
    ModelMetadata modelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => order, order.GetType());
    ModelValidator compositeValidator = ModelValidator.GetModelValidator(modelMetadata, base.ControllerContext);

    foreach (ModelValidationResult result in compositeValidator.Validate(null))
    {
        this.ModelState.AddModelError(result.MemberName, result.Message);
    }
}

Then ModelState.IsValid = true. compositeValidator.Validate() returns 0 errors. I think this indicates the model was not bound, but I still don't know why.

The controller method actually looks like this (I missed out the filter when originally writing this question)

[JsonFilter(Param = "order", JsonDataType = typeof(PurchaseOrder))] 
public JsonResult Save(PurchaseOrder order) { //  etc ... }

And the JsonFilter does this to extract the POCO from the json submitted data:

filterContext.ActionParameters[Param] 
    = jsSerializer.Deserialize(inputContent, JsonDataType);

I put a breakpoint on this line, and order is valid, plus order.OrderNumber has the correct value.

So still unresolved, but hopefully this extra info will help with finding a solution


Solution

  • Well I have "solved" it, but I do not really understand why the changes I made have helped.

    I had to do three things:

    1. Remove the json filter (filters don't bind)

    2. Change the contentType to application/json

      $.ajaxSetup({ contentType: "application/json; charset=utf-8" });

    3. Use the MVC futures download Microsoft.Mvc.dll as described here: http://haacked.com/archive/2010/04/15/sending-json-to-an-asp-net-mvc-action-method-argument.aspx. Where is says to add this to Application_Start() in Global.asax.cs:

      ValueProviderFactories.Factories.Add(new JsonValueProviderFactory());

    Now I dont know exactly why that has worked, but it has.

    Unfortunately it has had a negative side effect: the contenttype is applied to all $.get() and $.post() methods, and broken all my jqgrids - they only seem to work if the content type is the default of application/x-www-form-urlencoded

    So I've asked 2 follow on questions:

    1. Is it possible to set the content type in a $.post() call? Then I wouldn't need to set it globally Jquery - How to make $.post() use contentType=application/json?

    2. Is it possible to make jqrid work if the contenttype is application/json? Jquery - How to make $.post() use contentType=application/json?