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

3

9

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

Dematerialize answered 8/5, 2010 at 1:38 Comment(5)
why is the property marked virtual? Are you using POCO change tracking?Atman
What is the value? How are you using $.post? Does it work with normal submit?Nonobjective
I don't know why it is virtual, it was like that when I started working on the existing app.Dematerialize
Yes it works with a normal input type=submit buttonDematerialize
Code for manually validating the model was found here: digitallycreated.net/Blog/54/…Dematerialize
D
1

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?

Dematerialize answered 16/5, 2010 at 21:22 Comment(1)
Looks like I need to change my $.post()s that call a controller method to $.ajax(). But it just feels like I'm doing something wrong and it should just work if contentType is the default of application/x-www-form-urlencoded.Dematerialize
B
1

Are you sure that the error is coming from the OrderNumber field?

It seems strange that the error message has Order Number in 2 words.

Try changing the name of the field or removing the required tag to make sure that this is where the error is coming from.

Buke answered 11/5, 2010 at 8:27 Comment(2)
I believe that the MVC framework handles that for you auto-magically. Its the same as when you go Html.LabelFor(model.OrderNumber). The label says "Order Number" with a space between the words.Dematerialize
If I remove the [Required] annotation, then ModelState.IsValid == true. Its also the only field with this annotation.Dematerialize
R
1

There is a chance that your jQuery post is not posting in the right way back to the controller. You may be posting just the order number rather than an object that accurately represents the model. I'd have to see your javascript to be absolutely certain.

The correct way to post to that method would be (assuming your order number is stored in a javascript variable called "orderNumber":

$.post([url to method], 
       { order: { OrderNumber: orderNumber } }, 
       function(json) {
       //Insert code for actions to take after AJAX post returns.
       },
       'json');

If this is the case, then the next step I'd take is to double check that the javascript is indeed capturing the value of your order number and posting it as part of the request. A quick examination through Firebug should be able to tell you if that's happening or not.

Ricoriki answered 14/5, 2010 at 17:37 Comment(0)
D
1

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?

Dematerialize answered 16/5, 2010 at 21:22 Comment(1)
Looks like I need to change my $.post()s that call a controller method to $.ajax(). But it just feels like I'm doing something wrong and it should just work if contentType is the default of application/x-www-form-urlencoded.Dematerialize

© 2022 - 2024 — McMap. All rights reserved.