Binding to (and validating) HTTP headers with Web API 2
Published on June 8, 2015 by Kieren
Posted in Technology
It’s already possible of course to get access to HTTP header values in ASP.NET MVC or Web API controllers – the direct approach, as below, is to use Request.Headers within a controller action. There are some hoops to jump through though, since the header may not exist, and might have multiple values:
This is pretty fiddly, and things escalate quickly if we want to deal with multiple parameters. An example I’ve seen before is to use custom headers called X-Page-Size and X-Page-Number to handle pagination in some RESTful API. Even that is going to prove fiddly – it’s clear something more succinct should be possible and we should strive for simpler, more maintainable code.
The approach outlined in this blog post allows you to consider headers as a real part of your request model – they are parameters to your action, automatically populated by the framework. What’s more, by doing this we can take advantage of model binding functionality, including annotation-based validation of header values!
In short, with a few tiny reusable classes, we can write code such as the following:
While a custom FilterAttribute or some logic in a base controller type would be also be a viable approach, this method has the appealing property of the method signature working to document the API contract quite comprehensively. Here you can now have query string, request body and header values all available as action parameters in the method signature, with all validation working consistently. If you use constructs such as returning BadRequest(ModelState) in the event of a validation error, the validation messages returned to the caller are all in one consistent response object, too.
There are some complexities to the approach, not yet revealed:
- We need to map model member names to HTTP header names. In the example code shared here, to convert a member name to a HTTP header we’ll just insert a dash before each uppercase character (other than the first). This makes member XPageSize into header X-Page-Size (this could be extended to use an attribute to specify the header name, or alternative algorithm).
- To get the validation, we use the complex model binding, i.e. we specify a whole separate type to hold the model to bind to. But what if the type has a nested model, or an array? For now, we’ll just flatten everything and just map headers to properties at any level, which in model binding terms means ignoring “prefixes”.
Let’s get down to the code! The full code is available on our GitHub account – see the end of the article; for brevity, things are simplified a little below.
That pretty much wraps things up. The full source and a sample project are available on GitHub. Feel free to fork it, send us pull requests, and raise any issues if you run into any problems.