Web API complex parameter properties are all null

58,407

Solution 1

This seems to be a common issue in regards to Asp.Net WebAPI.
Generally the cause of null objects is the deserialization of the json object into the C# object. Unfortunately, it is very difficult to debug - and hence find where your issue is.
I prefer just to send the full json as an object, and then deserialize manually. At least this way you get real errors instead of nulls.
If you change your method signature to accept an object, then use JsonConvert:

public HttpResponseMessage Post(Object model)
        {
            var jsonString = model.ToString();
            PreferenceRequest result = JsonConvert.DeserializeObject<PreferenceRequest>(jsonString);
        }

Solution 2

So there are 3 possible issues I'm aware of where the value does not bind:

  1. no public parameterless constructor
  2. properties are not public settable
  3. there's a binding error, which results in a ModelState.Valid == false - typical issues are: non compatible value types (json object to string, non-guid, etc.)

So I'm considering if API calls should have a filter applied that would return an error if the binding results in an error!

Solution 3

Maybe it will help, I was having the same problem.

Everything was working well, and suddently, every properties was defaulted.

After some quick test, I found that it was the [Serializable] that was causing the problem :

public IHttpActionResult Post(MyComplexClass myTaskObject)
{
    //MyTaskObject is not null, but every member are (the constructor get called).
}

and here was a snippet of my class :

[Serializable]  <-- have to remove that [if it was added for any reason..]
public class MyComplexClass()
{
     public MyComplexClass ()
     {
        ..initiate my variables..
     }

     public string blabla {get;set;}
     public int intTest {get;set;
}

Solution 4

I guess problem is that your controller is expecting content type of [FromBody],try changing your controller to

public HttpResponseMessage Post(PreferenceRequest request)

and ajax to

$.ajax({
    type: "POST",
    data: JSON.stringify(request);,
    dataType: 'json',
    contentType: "application/json",
    url: "http://localhost:1111/service/User",

By the way creating model in javascript may not be best practice.

Solution 5

Using this technique posted by @blorkfish worked great:

public HttpResponseMessage Post(Object model)
    {
        var jsonString = model.ToString();
        PreferenceRequest result = JsonConvert.DeserializeObject<PreferenceRequest>(jsonString);
    }

I had a parameter object, which had a couple of native types and a couple more objects as properties. The objects had constructors marked internal and the JsonConvert.DeserializedObject call on the jsonString gave the error:

Unable to find a constructor to use for type ChildObjectB. A class should either have a default constructor, one constructor with arguments or a constructor marked with the JsonConstructor attribute.

I changed the constructors to public and it is now populating the parameter object when I replace the Object model parameter with the real object. Thanks!

Share:
58,407
Eddie
Author by

Eddie

Web developer and designer since 1994. C# dev for local government agency since 2006. Have been Java/JSP dev in past jobs.

Updated on July 09, 2022

Comments

  • Eddie
    Eddie almost 2 years

    I have a Web API service call that updates a user's preferences. Unfortunately when I call this POST method from a jQuery ajax call, the request parameter object's properties are always null (or default values), rather than what is passed in. If I call the same exact method using a REST client (I use Postman), it works beautifully. I cannot figure out what I'm doing wrong with this but am hoping someone has seen this before. It's fairly straightforward...

    Here's my request object:

    public class PreferenceRequest
    {
        [Required]
        public int UserId;
    
        public bool usePopups;
        public bool useTheme;
        public int recentCount;
        public string[] detailsSections;
    }
    

    Here's my controller method in the UserController class:

        public HttpResponseMessage Post([FromBody]PreferenceRequest request)
        {
            if (request.systemsUserId > 0)
            {
                TheRepository.UpdateUserPreferences(request.UserId, request.usePopups, request.useTheme,
                                             request.recentCount, request.detailsSections);
    
                return Request.CreateResponse(HttpStatusCode.OK, "Preferences Updated");                
            }
            else
            {
                return Request.CreateErrorResponse(HttpStatusCode.NotAcceptable, "You must provide User ID");
            }
        }
    

    Here's my ajax call:

    var request = {
        UserId: userId,
        usePopups: usePopups,
        useTheme: useTheme,
        recentCount: recentCount,
        detailsSections: details
    };
    
    $.ajax({
        type: "POST",
        data: request,
        url: "http://localhost:1111/service/User",
        success: function (data) {
            return callback(data);
        },
        error: function (error, statusText) {
            return callback(error);
        }
    });
    

    I've tried setting the dataType & contentType to several different things ('json', 'application/json', etc) but the properties of the request object are always defaulted or null. So, for example, if I pass in this object:

    var request = {
      UserId: 58576,
      usePopups: false,
      useTheme: true,
      recentCount: 10,
      detailsSections: ['addresses', 'aliases', 'arrests', 'events', 'classifications', 'custody', 'identifiers', 'phone', 'remarks', 'watches']
    }
    

    I can see a fully populated request object with the valid values as listed above. But in the Web API controller, the request is there, but the properties are as follows:

      UserId: 0,
      usePopups: false,
      useTheme: false,
      recentCount: 0,
      detailsSections: null
    

    FYI - I'm not doing ANY ASP.Net MVC or ASP.NET pages with this project, just using the Web API as a service and making all calls using jQuery $.ajax.

    Any idea what I'm doing wrong here? Thanks!

    UPDATE: I just want to note that I have many methods in this same Web API project in other controllers that do this exact same thing, and I am calling the exact same way, and they work flawlessly! I have spent the morning comparing the various calls, and there doesn't appear to be any difference in the method or the headers, and yet it just doesn't work on this particular method.

    I've also tried switching to a Put method, but I get the exact same results - the request object comes in, but is not populated with the correct values. What's so frustrating is that I have about 20 controller classes in this project, and the Posts work in all of those...

  • Eddie
    Eddie almost 10 years
    Thanks Anshul - I actually had started without the [FromBody] attribute, but added it later in hopes it would fix the issue. Unfortunately whether it's in or not I get the same result. I'm curious, if you are passing a complex object to a Web API method, how would you create the object to be used in the ajax call if not in javascript?
  • Eddie
    Eddie almost 10 years
    I tried this, and it did continue to work in my REST call using Postman, but from jQuery the model was null when it hit the Post method, so I got an exception in the Json.net deserialization attempt. I will start scrutinizing the headers in the two calls to see if I can figure out what is different in the jQuery call, or why the Postman call works...
  • Eddie
    Eddie almost 10 years
    Using this, along with Dalorzo's suggestion above for the client side, I was able to get it to work. Thank you!
  • Anshul Nigam
    Anshul Nigam almost 10 years
    Eddie you need to use a good MVVM(Model View View Model) library, I am using knockout.js knockoutjs.com and for mapping complex object to JavaScript I am using its mapping plugin knockoutjs.com/documentation/plugins-mapping.html. By using this i do not need to worry about model's be in sync
  • McGarnagle
    McGarnagle over 8 years
    Sheesh ... this seems like an elementary feature for a REST framework. How could Web API fail to get this right? Thanks for your workaround anyway!
  • tno2007
    tno2007 over 8 years
    I've spent half a day trying my posted model to work. blorkfish's method works 100% of the time. Thank you!
  • dmigo
    dmigo over 8 years
    Is there any other way to debug such an issue?
  • Michael Hilus
    Michael Hilus almost 8 years
    This is excellent to be a temporal workaround for debugging. After fixing serialization problems, the workaround can safely be removed again.
  • Donald.Record
    Donald.Record over 7 years
    This was it for me. Thank you for the post!
  • unruledboy
    unruledboy about 7 years
    you are the genius!
  • Rickless
    Rickless over 6 years
    I use Auto properties and I haven't came across such problem because of this! Do you have an actual experience with this, or this is just a hunch?
  • David McSpadden
    David McSpadden about 6 years
    This appears to be the correct solution when dealing with Kendo UI Grid
  • Pierre Nortje
    Pierre Nortje almost 6 years
    I received the same error. I realized my client was running on Newtonsoft v6 and my server was running on Newtonsoft v11. Once I updated my client to v11 my model was populated. Hope this helps someone.
  • Juanjo
    Juanjo almost 6 years
    Sorry But I cannot get this to work. Object is System.Object and ToString produces "System.Object" and the JsonConvert crashes. Am I missing something???
  • baHI
    baHI over 5 years
    Also there's a way to handle such errors easily + a possible way to distinguish between binding / validation errors. see: stackoverflow.com/questions/45941246/…
  • baHI
    baHI over 5 years
    Yes, 1) [FromBody] 2) a parameterless constructor (in most cases can be even protected, but would check that later, once issue fixed) and also 3) all properties must have a public settter! In the RespuestaComparacion i'd use bool? (nullable) if the NULL value from the client should be a valid one.
  • Delorian
    Delorian over 5 years
    Good on you @baHI for collating answers. Please see my post below as another possible issue people may be having.
  • gemini88mill
    gemini88mill about 5 years
    I used a JArray and not a JObject, but I'm upvoting this becasue it got me to my original error.
  • chengzi
    chengzi about 5 years
  • Scott Fraley
    Scott Fraley almost 5 years
    Ugh. I added [FromBody] AND JObject and yet my collection property is still null. (The other property, a simple int comes across just fine.) :(
  • Ahhzeee
    Ahhzeee over 4 years
    What if your Http method was a post? And the handler for the httppost is a razor page. what would you do then?
  • Craig
    Craig about 4 years
    Adding the 'Content-Type': 'application/json' header fixed this problem for me.
  • balron
    balron over 3 years
    thanks my main class was not set as public. It was the cause of the problem
  • Nick Franceschina
    Nick Franceschina over 3 years
    arggghhh... "public settable"... impossible to debug! #madness