Suppose you had an API like shown below
You can easily post a Person to that action method using JSON through $ajax using jQuery. But the bigger question is how ASP.NET MVC transforms the JSON payload or forms payload to required object. Under the hood, ASP.NET MVC uses the DefaultModelBinder in combination with the JsonValueProviderFactory to bind that value.Then question arises Why simply deserialize the incoming JSON request directly to the model (Person)? There are couple of ways in which data gets posted
- application/x-www-form-urlencoded – When you submit a typical HTML form, the content type of that submission is application/x-www-form-url-encoded. 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. When content is posted in this format, the DefaultModelBinder calls into the FormValueProvider asking for a value for each property of the model
- application/json – Another content type in which u can post data is through JSON objects like shown below
When this code executes, 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 Person instance, but ASP.NET MVC does not do that, reason for it is has to perform validation on input values, if it does not then the serialization would fail. 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 to solve this 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. All the ket value pairs form-encoded and JSON are coverted to type IValueProvider (Dictionary) and DefaultModelBinder already knows how to bind those values to a type while providing property level validation.
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.”
Model binders aren’t locked to individual sources they can build objects from an aggregate of sources for e.g. if you person object is posted and a client makes a JSON POST request to an action method with the url /person/update/1 with the following content:
The DefaultModelBinder will pull the Id value from the RouteData and the FirstName, LastName and Email 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.
In Summary – 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.