Today, let’s kick off a series intended to look at different aspects of HTTP parameter binding in ASP.NET Web API. Why? Aside from the awesome series by Mike Stall, there isn’t really that much material on the web on this particular subject. And developers coming from MVC background, often get surprised by differences in the model binding mechanism between MVC and Web API.

In this first post, let’s have a brief overview of parameter binding in Web API and then specifically look at binding model from URI.

More after the jump.

Model binding with MVC

In MVC, the model binding process is done against both the body and the URI. In other words, the framework will look for model properties in both places, and try to stitch everything together. This is possible because MVC would buffer the body of the request and that would allow it to pick pieces of key value pairs (effectively, each value in the request is part of the name value collection) and try to compose models out of them.

Consider the simple example.

C#

1

2

3

4

5

publicclassPerson

{

publicstringFirstName{get;set;}

publicstringLastName{get;set;}

}

In MVC you can send the following request:

C#

1

2

3

POST/person/index?FirstName=Filip

Content-Type:application/json

{"LastName":"W"}

This will correctly bind itself to an action with this signature:

C#

1

2

[HttpPost]

publicJsonResult Index(Personp){}

You can also easily bind the whole model just from the URI, which is particularly useful on GET requests where you might be passing in a complex sets of conditions. Conversely, you can bind the entire model just from the body too.

HttpParameterBinding

In Web API, the core concept behind binding parameters is HttpParameterBinding, which, out of the box, can execute either as a model binder, or through the use of a formatter.

In practice, this means that when Web API pipeline runs, a default implementation of IActionValueBinder will determine whether each specific parameter will be bound using the URI (model binding) or request’s body (formatter). To be even more precise, this process will not happen on every request, because in Web API binding can be statically determined for each parameter. Therefore, it will really happen just once – through a call to ApiControllerActionSelector, which will cache the resulting ActionDescriptor inside a private ActionSelectorCacheItem class. The subsequent requests will then be fed from that cache.

We’ll look at what happens when the formatter type of binding is selected in the next post. For now let’s focus on what happens when Web API decides to use ModelBinder and bind from URI.

Web API will look for any ValueProviderFactories registered against the Services property of HttpConfiguration.

A factory returns IValueProvider, which can be used to compose an object from the HTTP request using the most basic building block – a string key/value present in the HTTP request. The main role of ValueProviders is to abstract away the logic of retrieving the information from the HTTP request from ModelBinders and just feed them with individual bits used to compose more complex objects.

C#

1

2

3

4

5

publicinterfaceIValueProvider

{

boolContainsPrefix(stringprefix);

ValueProviderResult GetValue(stringkey);

}

By default, Web API ships with two registered factories that take part in the URI parameter bindings:

– QueryStringValueProviderFactory

– RouteDataValueProviderFactory

And their names & roles are rather self explanatory.

On a side note, it’s worth mentioning, that if the split into model binders / formatters is not enough for you, you can also introduce your own versions of HttpParameterBinding – and we’ll do that in the later posts in this series.

Understanding Web API parameter binding

So what happens if you just stick some parameters into your action? How does IActionValueBinder determine whether to use a formatter or a model binder?

– collections of simple parameters are by default read from the body too

– you cannot compose a single model based on input from both URI and request body, it has to be one or the other

While some of these default conventions may seem limiting and unintuitive, there are just enough customization hooks to allow you as a developer to override them.

Binding from URI

In order to bind a model (an action parameter), that would normally default to a formatter, from the URI you need to decorate it with either [ModelBinder] or [FromUri] attribute.

FromUriAttribute simply inherits from ModelBinderAttribute, providing you a shortcut directive to instruct Web API to grab specific parameters from the URI using the ValueProviders defined in the IUriValueProviderFactory. The attribute itself is sealed and cannot be extended any further, but you add as many custom IUriValueProviderFactories as you wish.

Mike Stall has a great post on creating a custom value provider for Web API.

Let’s use a ProductFilter as an example:

C#

1

2

3

4

5

6

7

8

publicclassProductFilter

{

publicint?PageIndex{get;set;}

publicint?PageSize{get;set;}

publicstring[]Sizes{get;set;}

publicdecimal?MinPrice{get;set;}

publicdecimal?MaxPrice{get;set;}

}

Just through the use of [FromUri] you are able to bind the following types entirely from the URI:

1) complex types, such as our ProductFilter

C#

1

2

3

publicHttpResponseMessage Get([FromUri]Productfilter filter){}

GET/products?pageindex=2&minprice=10&sizes=xl&sizes=xxl

This will bind correctly to the type we declared, leaving other properties as null (since we explicitly declared them as nullable).

Notice we used the default ValueProviders and just split the string extracted by the provider in the ModelBinder itself. You could potentially split the string inside a custom value provider too.

Defaulting to [FromUri] for GET and HEAD requests

Web API will always try to eagerly bind non-primitive and non-string covnertible types from the body of the request. While in many cases that’s all right, semantically, it doesn’t make much sense for GET and HEAD requests, since in accordance to the HTTP specification, these are body-less requests.

The decision whether a parameter should be bound from URI or body happens inside the DefaultActionValueBinder (the default implementation of the aforementioned IActionValueBinder), which, as almost every infrastructure piece in Web API, is extensible and overridable.

Going back to our previous example, we’d like the GET request to be written without a necessity for [FromUri].

C#

1

publicHttpResponseMessage Get(Productfilter filter){}

To achieve this we can introduce a small custom action value binder, extending the default one:

With this trivial change, we tell Web API that if a given action is GET/HEAD type of action, always bind as if [FromUri] was present, otherwise, we let the base logic flow.

Using MVC style binding with Web API

If you feel that MVC style binding is something you’d need in your Web API, you can achieve it by incorporating a terrific MvcActionValueBinderby Mike Stall into your application. It is also now part of the WebApiContrib project, so can be instantly pulled from Nuget.

I have this mind boggling issue about IModelBinder you presented here. It works in another app but in this particular app its doesn’t. I tried to debug it and put a breakpoint but it doesn’t step into the class. Looks like something is preventing it from working/invoking.

Hope theres something you may be able to share.

Thanks
Roel

couten

What happened to part 2 and 3?

http://www.strathweb.com/ Filip W

oh, I have forgotten about that

JasonMing

“4) apparently it also works with dictionaries, but I honestly don’t know how ”

I tried, it can’t bind to dictionaries by FromUri attribute, seems caused by routing mechanism, the binder alway cannot find the correct parameters from query string which names like the parameter.
To bind to a dictionary, it seems no way unless you wrote a custom ParameterBindingAttribute.

If you feel that it is a bit too long and hard to remember how to write “[ModelBinder(typeof(CommaDelimitedCollectionModelBinder))]” each time you have a comma separated URI, you can also make the class extend System.Attribute like this: