Binding to (and validating) HTTP headers with Web API 2

Published on June 8, 2015 by Kieren

Posted in Technology

Kieren Johnstone

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:

[code lang=”C#”][HttpGet]
[Route(“products”)]
public IHttpActionResult GetProducts([FromUri]string filter)
{
string headerValue = null;
IEnumerable headerValues;
if (Request.Headers.TryGetValue(“X-Secret-Penguin-Handshake”, out headerValues))
{
headerValue = headerValues.FirstOrDefault();
}
if (string.IsNullOrEmpty(headerValue)) return BadRequest(“Wenk!”);
var results = RunGetProductsQuery(filter);
return Json(results);
} [/code]

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:

[code lang=”C#”][HttpGet]
[Route(“products”)]
public IHttpActionResult GetProducts([FromUri]string filter, [FromHeader]StandardGetHeaders headers)
{
// validate headers (and any other bound fields), returning useful messages
// upon failure
if (!ModelState.IsValid) return BadRequest(ModelState);

// run our query
var results = RunGetProductsQuery(
filter,
headers.XPageNumber,
headers.XPageSize);

return Json(results);
}

// …

// this is a supporting “header model” class – to receive header values (if present)
// and define validation rules in a standardized way
public class StandardGetHeaders
{
[Required(“Wenk!”)]
public string XSecretPenguinHandshake { get; set; }

[Range(1, int.MaxValue, “Page number must be greater than 0”)]
public int XPageNumber { get; set; }

[Range(1, 100, “Page size must be between 1 and 100”)]
public int XPageSize { get; set; }
}
[/code]

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:

  1. 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).
  2. 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.

[code lang=”C#”]// the attribute itself – to put against our action parameters.
// combines the model binding engine with our value provider factory.
public class FromHeaderAttribute : ParameterBindingAttribute
{
public override HttpParameterBinding GetBinding(HttpParameterDescriptor parameter)
{
var httpConfig = parameter.Configuration;
var binder = new ModelBinderAttribute()
.GetModelBinder(httpConfig, parameter.ParameterType);
return new ModelBinderParameterBinding(
parameter, binder,
new ValueProviderFactory[] { new HeaderValueProviderFactory() });
}
}

// factory for creating our custom value provider class given a specific action
// context. this is where we capture and keep reference to the HTTP headers
// for the request .
public class HeaderValueProviderFactory : ValueProviderFactory
{
public override IValueProvider GetValueProvider(HttpActionContext actionContext)
{
return new HeaderValueProvider(actionContext.Request.Headers);
}
}

// Our value provider, which handles the bulk of the work
public class HeaderValueProvider : IValueProvider
{
private readonly HttpRequestHeaders _headers;

public HeaderValueProvider(HttpRequestHeaders headers)
{
_headers = headers;
}

public bool ContainsPrefix(string prefix)
{
// all prefixes are flattened – all members and sub-members
// considered equally
return true;
}

// the heart of the approach. this will be called for each property of the
// model we’re binding to – we need only find and return the appropriate
// values
public ValueProviderResult GetValue(string key)
{
IEnumerable values;

var propName = RemovePrefixes(key);
var headerName = MakeHeaderName(propName);

if (!_headers.TryGetValues(headerName, out values))
{
return null;
}

var data = string.Join(“,”, values.ToArray());
return new ValueProviderResult(values, data, CultureInfo.InvariantCulture);
}

private static string RemovePrefixes(string key)
{
var lastDot = key.LastIndexOf(‘.’);
if (lastDot == -1) return key;

return key.Substring(lastDot + 1);
}

// here’s the simple algorithm for making a HTTP header name out of our members:
// iterate through the characters, inserting a dash before uppercase letters,
// with the exception of the first character
private static string MakeHeaderName(string key)
{
var headerBuilder = new StringBuilder();

for (int i = 0; i < key.Length; i++) { if (char.IsUpper(key[i]) && i > 0)
{
headerBuilder.Append(‘-‘);
}
headerBuilder.Append(key[i]);
}

return headerBuilder.ToString();
}
}
[/code]

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.