When I need to access information from the Request in a controller action in ASP.NET Web API, the first temptation is to access the request object directly and look for what I need. The main problem with doing this is that it turns writing any kind of automated test for this action into hell. For example:
[HttpGet]
[Route("doAnything")]
public IHttpActionResult Get()
{
var referrer = request.Headers.Referrer
var resul = .....
// DO ANY STUFF WITH THE REFERRER
return result;
}
The simplest mechanism I can think of to make this action easy to test is to have the piece of data I need from the request reach the action as a parameter:
public IHttpActionResult Get(string referrer)
With this change I can write a test for this action without any problem and call it a day. But of course, what happens when I run the code in Web API? Obviously it fails. This is where Web API gives me a hand and lets me do something like this:
public IHttpActionResult Get([ValueProvider(typeof(ReferrerValueProviderFactory))]string referrer)
Thanks to Value Providers, Web API can inject the value I need into the parameter. This would be the code:
public class RefererValueProviderFactory : ValueProviderFactory {
public override IValueProvider GetValueProvider(HttpActionContext actionContext) {
return new RefererValueProvider(actionContext.Request.Headers);
}
}
public class RefererValueProvider : IValueProvider {
private readonly HttpRequestHeaders headers;
public RefererValueProvider(HttpRequestHeaders headers) {
this.headers = headers;
}
public bool ContainsPrefix(string prefix) {
return true;
}
public ValueProviderResult GetValue(string key) {
return new ValueProviderResult(headers.Referrer, headers.Referrer.ToString(), CultureInfo.InvariantCulture);
}
}
The code is simplified for the example, but you need to add error handling. For example, the header might be null, which would throw an exception when calling ToString().
For what I have so far to work, the only thing left is to register the new value provider in the Register method of WebApiConfig:
public static void Register(HttpConfiguration config)
{
config.Services.Add(typeof(ValueProviderFactory), new ReferrerValueProviderFactory());
...
The problem I see so far is that using the [ValueProvider] attribute hurts readability, and having to register it adds a bit of complexity to the setup and one more thing I’d have to remember in the next project. Given how things are done in Web API, I’d rather have something like this:
public IHttpActionResult Get([FromReferrerHeader]string referrer)
Something like this is much more readable and in line with what I’m already used to when I use attributes like [FromBody] or [FromUri]. This is where Web API invites me to use Parameter Binding. Basically, what I’m going to do is create an attribute that inherits from ParameterBindingAttribute and use the ValueProvider I created just before to get this piece of data:
public class FromReferrerHeaderAttribute : 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 ReferrerValueProviderFactory() });
}
}
Also, when using parameter binding, you no longer need to register the value provider, which makes it simpler to use.