Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / Javascript

Public Field Not Bound When Posting to WebAPI (or a deep dive into WebAPI model binding)

5.00/5 (1 vote)
1 Dec 2013CPOL3 min read 13.2K  
Public Field Not Bound When Posting to WebAPI (or a deep dive into WebAPI model binding)

Introduction

When trying to create a sandbox project using WebAPI (on MVC4), I was struggling with a weird problem: My data wasn't being received in the server. I had the following jQuery call:

JavaScript
$.post("api/Values", {value1:1, value2:2}, function(result){ console.log(result); })

and the WebAPI service action that I was targeting was something like this:

C#
public IEnumerable<string> Post(Dummy value)
{
 return new string[] { value.Value1, value.Value2 };
}

I noticed that even the instance of Dummy was being created, Value1 and Value2 where always null. The Dummy class was:

C#
public class Dummy
{
 public string Value1;
 public string Value2;
}

Pretty simple, right? Well, after doing a lot of research, I changed by accident one of the Dummy fields to become a property:

C#
public class Dummy
{
 public string Value1;
 public string Value2 {get;set;}
}

I tested again and Voilà!!... well, half voilà actually... When posting, now I was receiving data in Value2, but still not in Value1. This was really intriguing... how come property was being assigned correctly but not the field? Both are public, right? Why the difference?

Obviously, I knew the solution was changing both fields to be properties now, but I wanted to know why that was happening. I started digging on how WebAPI works and found a really interesting Web API poster, that describes the full lifecycle of a HTTP message. There I got my first clue, so I started researching on how ModelBinding happens. As described there, one of the binding methods is MediaTypeFormatter. Since I was sending JSON object, I tested the Deserialization process based on the test methods provided in the WebAPI overview site.

C#
T Deserialize<t>(MediaTypeFormatter formatter, string str) where T : class
{
 // Write the serialized string to a memory stream.
 Stream stream = new MemoryStream();
 StreamWriter writer = new StreamWriter(stream);
 writer.Write(str);
 writer.Flush();
 stream.Position = 0;
 // Deserialize to an object of type T
 return formatter.ReadFromStreamAsync(typeof(T), stream, null, null).Result as T;
}

passing the same JSON object that I had on my jQuery call. The result: The method assigned successfully the values for both the field and the property. By inspecting the HTTP Request headers, I found out that data wasn't being actually sent as JSON but in the following format: Content-Type:application/x-www-form-urlencoded; charset=UTF-8, which tells the server that data is being sent like this: Value1=1&Value2=2. Then, we need to change the AJAX call to be like this:

JavaScript
$.ajax({
  url: "api/Values",
  data: JSON.stringify({Value1:1,Value2:2}),
  type: "POST",
  contentType:"application/json; charset=utf-8"
})

Please notice 2 things: I changed the contentType for the request AND Stringified the JSON object. By making these changes, Dummy public fields were now populated correctly.
Now, I still wanted to know why my values weren't bound when I wasn't specifying the request content type. Doing more research, I found this really interesting article by Mike Stall called How WebAPI does parameter binding which states:

There are 2 techniques for binding parameters: Model Binding and Formatters. In practice, WebAPI uses model binding to read from the query string and Formatters to read from the body.

If you are not yet bored, you might remember that when we didn't specify the request content type, the data was being sent as Content-Type:application/x-www-form-urlencoded; charset=UTF-8. This means, that WebAPI was using ModelBinding (and not formatters) to populate the Dummy instance. Moreover, the article has another interesting declaration:

ModelBinding is the same concept as in MVC, [...]. Basically, there are “ValueProviders” which supply pieces of data such as query string parameters, and then a model binder assembles those pieces into an object.

And how does ModelBinding work in MVC? That was my next question. And I was really happy that Microsoft open-sourced the ASP.NET WebStack, because there is where we can find the answer. If we look into DefaultModelBinder source code, we'll find that when talking about complex models, it only looks for the object properties to populate the data (maybe because having public fields is a bad practice).
Well, I hope you can find this post as interesting as I found learning all this. Sometimes, making silly errors can drive you into learning really interesting things.

Useful References

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)