What’s the Difference Between a Value Provider and Model Binder?

ASP.NET MVC 3 introduced the ability to bind an incoming JSON request to an action method parameter, which is something I wrote about before. For example, suppose you have the following class defined (keeping it really simple here):

public class ComicBook {
  public string Title { get; set; }
  public int IssueNumber { get; set; }
}

And you have an action method that accepts an instance of ComicBook:

[HttpPost]
public ActionResult Update(ComicBook comicBook) {
  // Do something with ComicBook and return an action result
}

You can easily post a comic book to that action method using JSON.

Under the hood, ASP.NET MVC uses the DefaultModelBinder in combination with the JsonValueProviderFactory to bind that value.

A question on an internal mailing list recently asked the question (and I’m paraphrasing here), “Why not cut out the middle man (the value provider) and simply deserialize the incoming JSON request directly to the model (ComicBook in this example)?”

Great question! Let me provide a bit of background to set the stage for the answer.

Posting Content to an Action

There are a couple of different content types you can use when posting data to an action method.

application/x-www-form-urlencoded

You may not realize it, but when you submit a typical HTML form, the content type of that submission is application/x-www-form-url-encoded.

As you can see in the screenshot below from Fiddler, the contents of the form is posted as a set of name value pairs separated by ampersand characters. The name and value within each pair are separated by an equals sign.

By the time you typically interact with this data (outside of model binding), it’s in the form of a dictionary like interface via the Request.Form name value collection.

The following screenshot shows what such a request looks like using Fiddler.

form-encoded-post

When content is posted in this format, the DefaultModelBinder calls into the FormValueProvider asking for a value for each property of the model. The FormValueProvider is a very thin abstraction over the Request.Form collection.

application/json

Another content type you can use to post data is application/json. As you might guess, this is simply JSON encoded data.

Here’s an example of a bit of JavaScript I used to post the same content as before but using JSON. Note that this particular snippet requires jQuery and a browser that natively supports the JSON.stringify method.

<script type="text/javascript">
    $(function() {
        var comicBook = { Title: "Groo", IssueNumber: 101 }
        var comicBookJSON = JSON.stringify(comicBook);
        $.ajax({
            url: '/home/update',
            type: 'POST',
            dataType: 'json',
            data: comicBookJSON,
            contentType: 'application/json; charset=utf-8',
        });
    });
</script>

When this code executes, the following request is created.

json-encoded-post

Notice that the content is encoded as JSON rather than form url encoded.

JSON is a serialization format so it’s in theory possible that we could straight deserialize that post to a ComicBook instance. Why don’t we do that? Wouldn’t it be more efficient?

To understand why, let’s suppose we did use serialization and walk through a common scenario. Suppose someone submits the form and they enter a string instead of a number for the field IssueNumber. You’d probably expect to see the following.

form-validation-error

Notice that the model binding was able to determine that the Title was submitted correctly, but that the IssueNumber was not.

If our model binder deserialized JSON into a ComicBook it would not be able to make that determination because serialization is an all or nothing affair. When serialization fails, all you know is that the format didn’t match the type. You don’t have access to the granular details we need to provide property level validation. So all you’d be able to show your users is an error message stating something went wrong, good luck figuring out what.

The Solution

Instead, what we really want is a way bind each property of the model one at a time so we can determine which of the fields are valid and which ones are in error. Fortunately, the DefaultModelBinder already knows how to do that when working with the dictionary-like IValueProvider interface.

So all we need to do is figure out how to expose the posted JSON encoded content via the IValueProvider interface. As I wrote before, Jonathan Carter had the bit of insight that provided the solution to this problem. He realized that you could have the JSON value provider deserialize the incoming JSON post to a dictionary. Once you have a dictionary, it’s pretty easy to implement IValueProvider and the DefaultModelBinder already knows how to bind those values to a type while providing property level validation. Score!

Value Provider Aggregation

The answer I provided only tells part of the story of why this is implemented as a value provider. There’s another aspect that was illustrated by my co-worker Levi. Sadly, for someone so gifted intellectually, he has no blog, so I’ll paraphrase his words here (with a bit of verbatim copying).

As I mentioned earlier, value providers provide an abstraction over where values actually come from. Value providers are responsible for aggregating the values that are part of the current request, e.g. from Form collection, the query string, JSON, etc.  They basically say “I don’t know what a ‘FirstName’ is for or what you can do with it, but if you ask me for a ‘FirstName’ I can give you what I have.”

Model binders are responsible for querying the value providers and building up objects based on those results.  They basically say “I don’t know where directly to find a ‘FirstName’, ‘LastName’, or ‘Age’, but if the value provider is willing to give them to me then I can create a Person object from them.”

Since model binders aren’t locked to individual sources (with some necessary exceptions, e.g. HttpPostedFile), they can build objects from an aggregate of sources. If your Person type looks like this:

public class Person {
  int Id { get; set; }
  [NonNegative]
  int Age { get; set; }
  string FirstName { get; set; }
  string LastName { get; set; }
}

And a client makes a JSON POST request to an action method (say with the url /person/edit/1234 with the following content:

{ 
  "Age": 30, 
  "FirstName": "John", 
  "LastName": "Doe" 
} 

   
The DefaultModelBinder will pull the Id value from the RouteData and the Age, FirstName, and LastName values from the JSON when building up the Person object. Afterwards, it’ll perform validation without having to know that the various values came from different sources.

Even better, if you wrote a custom Person model binder and made it agnostic as to the current IValueProvider, you’d get the correct behavior on incoming JSON requests without having to change your model binder code one tiny iota.  Neither of these is possible if the model binder is hard-coded to a single provider.

TL;DR Summary

The goal of this post was to provide a bit of detail around an interesting aspect of how ASP.NET MVC turns strings sent to a web server into strongly typed objects passed into your action methods.

Going back to the original question, the answer is simply, we use a value provider for JSON to enable property level validation of the incoming post and also so that model binding can build up an object by aggregating multiple sources of data without having to know anything about those sources.

What others have said

Requesting Gravatar... Jim Davies Jun 30, 2011 4:44 AM
# re: What’s the Difference Between a Value Provider and Model Binder?
Many thanks Phil - very clear and enlightening.

Typo near the end? TL;DR Summary

Best,
Jim
Requesting Gravatar... Chris V. Jun 30, 2011 4:56 AM
# re: What’s the Difference Between a Value Provider and Model Binder?
This is a great post. I love getting perspective on implementation details like this.

On a related note, one thing that's always bothered me about the default model binder code (though I admit, not enough for me to look at the source code and figure out a way around it), is that if you have an integer property to be bound, and the user submits a non-integer value, the error message returned seems to come from deep within the framework somewhere. This error message does not seem configurable via code because the error is thrown before the model-based validation ever actually occurs. Maybe you could shed some light on that one?
Requesting Gravatar... Gurdeep Singh Jun 30, 2011 5:51 AM
# re: What’s the Difference Between a Value Provider and Model Binder?
Very well explained. Thanks for the write up! Still wondering what if the browser do not support json.stringify natively? Resort back to plugins? Though, I myself use very little json for post backs and that too without the need of any stringifiers.
Requesting Gravatar... haacked Jun 30, 2011 8:33 AM
# re: What’s the Difference Between a Value Provider and Model Binder?
@Chris You are right. That error comes when we attempt type coercion. We're going to fix the fact that you can't configure that message in ASP.NET MVC 4.
Requesting Gravatar... haacked Jun 30, 2011 8:35 AM
# re: What’s the Difference Between a Value Provider and Model Binder?
@Jim TL;DR stands for Too Long, Didn't Read

@Gurdeep You should probably use the json2.js library as described here. I was just being lazy in my blog post and demonstrating the principle, not the code I would use in production.
Requesting Gravatar... Anonymious Jun 30, 2011 4:16 PM
# re: What’s the Difference Between a Value Provider and Model Binder?
Not know whether you are aware about this bug present in http://bit.ly/lkAIyb, which will bypass ASP.NET Request Validation if you are using Json Value Provider.
Requesting Gravatar... av Jul 02, 2011 10:03 AM
# re: What’s the Difference Between a Value Provider and Model Binder?
In FUBUMVC you wouldn't even have to worry about it.....

Haha..just had to be the first one who FUBU'ed the post
Requesting Gravatar... Petar Repac Jul 02, 2011 6:47 PM
# re: What’s the Difference Between a Value Provider and Model Binder?
This is one of those aha!!! posts!
Thank you for clear explanation.
Requesting Gravatar... Roatin Marth Jul 04, 2011 12:22 AM
# re: What’s the Difference Between a Value Provider and Model Binder?
One problem with shoe-horning JSON request bodies into IValueProvider is that incoming enum values (given as ints) do not get bound to the target property. JSON requests get a DictionaryValueProvider&lt;object&gt;. The model binder asks the value provider to convert it's value to the enum type via valueProviderResult.ConvertTo(enumType)).

The value provider gets an EnumConverter for the TypeConverter, but alas, when given an int object the EnumConverter doesn't know what to do with it. No other ValueProvider has this behaviour since they all store their raw values as string objects.
Requesting Gravatar... bill Jul 04, 2011 2:41 AM
# re: What’s the Difference Between a Value Provider and Model Binder?
So, with this ability, whats the value prop of WCF over MVC with json objects?
Requesting Gravatar... Mazhar Jul 06, 2011 8:28 AM
# re: What’s the Difference Between a Value Provider and Model Binder?
Aha!!! (same as Peter!)
Requesting Gravatar... Rais Hussain Jul 06, 2011 6:39 PM
# re: What’s the Difference Between a Value Provider and Model Binder?
@haacked Nice post and clear explanation of internals of ASP.NET MVC haacks!!!
Thanks Phil.
Requesting Gravatar... ZyZ Jul 07, 2011 1:19 AM
# re: What’s the Difference Between a Value Provider and Model Binder?
@chris: You can configure those error messages, however it is more complicated than it should be

1) When application starts register resource that will handle binding errors
DefaultModelBinder.ResourceClassKey = "ResourceNameHere";

2) Create "ResourceNameHere" resource in App_GlobalResources

3) Resource should provide values for 2 keys "PropertyValueInvalid"
and "PropertyValueRequired"
Requesting Gravatar... Aaron Nov 26, 2011 2:36 AM
# re: What’s the Difference Between a Value Provider and Model Binder?
Just a bit of feedback on the Json Value Provider introduction. I need to use a different JSON parser ( Newtonsoft is now using the new SerializationBinder.BindToName which offers pretty clean polymorphic JSON data models ) than the one provided by MS and after a few hours of digging it turned out that the built-in JsonValueProviderFactory is what was internally not providing the HTTP POSTed JSON data to the alternate ModelBinder (Request.InputStream was empty) that I created to use my own JSON parser.

Once I removed JsonValueProviderFactory from the ValueProviderFactories.Factories I was getting nice clean object bindings with my own DefaultModelBinder. I understand the seatbelt it is supposed to provide but in addition to the security issue that was also found (weblogs.asp.net/...) it may be worth an internal thread to discuss taking it out or making it in an opt-in.
Requesting Gravatar... Asif Ashraf Jan 30, 2012 9:01 PM
# re: What’s the Difference Between a Value Provider and Model Binder?
Cool, fixed my issue. thanks

What do you have to say?

(will show your gravatar)
Please add 8 and 7 and type the answer here: