Web API complex parameter properties are all null
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:
- no public parameterless constructor
- properties are not public settable
- 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!
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, 2022Comments
-
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 almost 10 yearsThanks 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 almost 10 yearsI 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 almost 10 yearsUsing this, along with Dalorzo's suggestion above for the client side, I was able to get it to work. Thank you!
-
Anshul Nigam almost 10 yearsEddie 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 over 8 yearsSheesh ... 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 over 8 yearsI've spent half a day trying my posted model to work. blorkfish's method works 100% of the time. Thank you!
-
dmigo over 8 yearsIs there any other way to debug such an issue?
-
Michael Hilus almost 8 yearsThis is excellent to be a temporal workaround for debugging. After fixing serialization problems, the workaround can safely be removed again.
-
Donald.Record over 7 yearsThis was it for me. Thank you for the post!
-
unruledboy about 7 yearsyou are the genius!
-
Rickless over 6 yearsI 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 ahunch
? -
David McSpadden about 6 yearsThis appears to be the correct solution when dealing with Kendo UI Grid
-
Pierre Nortje almost 6 yearsI 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 almost 6 yearsSorry 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 over 5 yearsAlso there's a way to handle such errors easily + a possible way to distinguish between binding / validation errors. see: stackoverflow.com/questions/45941246/…
-
baHI over 5 yearsYes, 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 over 5 yearsGood on you @baHI for collating answers. Please see my post below as another possible issue people may be having.
-
gemini88mill about 5 yearsI used a JArray and not a JObject, but I'm upvoting this becasue it got me to my original error.
-
chengzi about 5 yearsstackoverflow.com/questions/28557063/…, this is useful
-
Scott Fraley almost 5 yearsUgh. I added
[FromBody]
ANDJObject
and yet my collection property is still null. (The other property, a simpleint
comes across just fine.) :( -
Ahhzeee over 4 yearsWhat if your Http method was a post? And the handler for the httppost is a razor page. what would you do then?
-
Craig about 4 yearsAdding the 'Content-Type': 'application/json' header fixed this problem for me.
-
balron over 3 yearsthanks my main class was not set as public. It was the cause of the problem
-
Nick Franceschina over 3 yearsarggghhh... "public settable"... impossible to debug! #madness