Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web

Web API - Custom header value provider

5.00/5 (2 votes)
24 May 2013CPOL3 min read 36.7K  
How to pass complex object to server side using GET request.

Introduction 

There are cases when you have to send a complex object to server side using GET requests. In order to achieve that you would use the URI binding by decorating the controller method parameter  with [FromURI] attribute.  So if you have an object called "Criteria" which is composed from "CriteriaA" and" CriteriaB" properties, your GET request would look like this: http://myapplication/api/getfoobycriteria?CriteriaA=valueA&CriteriaB=valueB.

The other way

Now what if the object you want to send is a bit to complex or to big in its serialized form? The URI binding could still be used just that you would end up with a quite ugly/long URL which for some might not be an issue. What if GET request should not be cached by the browser? In that case the appropriate headers would have to be used in order to inform the browser that the responses  from GET request should not be cached. There is a different way to send complex objects to server side using GET requests. Same functionality can be achieved by using a custom header which would hold the serialized complex object. That custom header would be then sent to server side where along the pipeline of Web API will be de-serialized and bounded to the invoked controller method parameter. In order to achieve the previously mention functionality a custom header value provider would have to be implemented. There is one interface and one class, both part of System.Web.Htpp.ValueProviders namespace which will be used, IValueProvider and ValueProviderFactory. The role of value providers is to get raw data from incoming request and pass it to model binders, where model binders will convert the raw value to appropriate data types (complex objects,   collections of complex object, etc) by using various mechanisms such as de-serialization, type coercion and so on. Here it is the code for our custom generic HeaderValueProvider:

C#
using System;
using System.Globalization;
using System.Net.Http.Headers;
using System.Linq;
using System.Web.Http.ValueProviders;
using Newtonsoft.Json;
 
namespace MyProject.Infrastructure.WebAPI
{
    public class HeaderValueProvider<t> : IValueProvider where T : class
    {
        private const string HeaderPrefix = "X-";
        private readonly HttpRequestHeaders _headers;

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

        public bool ContainsPrefix(string prefix)
        {
            var contains = _headers.Any(h => h.Key.Contains(HeaderPrefix + prefix));
            return contains;
        }
 
        public ValueProviderResult GetValue(string key)
        {
                var contains = _headers.Any(h => h.Key.Contains(HeaderPrefix + key));
                if (!contains)
                    return null;
 
                var value = _headers.GetValues(HeaderPrefix + key).First();
                var obj = JsonConvert.DeserializeObject<t>(value, 
                  new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
                return new ValueProviderResult(obj, value, CultureInfo.InvariantCulture);
        }
    }
}

First of all, the HeaderValueProvider class has a constructor which accepts one parameter, that is of HttpRequestHeaders type which is a collection of request headers. This is the place where the custom header will be searched for. The HeaderValueProvider class is really simple, beside the constructor it has only two methods, ContainsPrefix and GetValue. It is important to note that the custom headers name should be prefixed, in this case "X-" represents the prefix used. The ContainsPrefix method does nothing else except to check if any of the request headers is a custom one based on the defined prefix. It returns true if the request headers collection contains a custom header or false if it does not. The last method of HeaderValueProvider is GetValue with only one parameter of type string (string key). This is the key which composed with the prefix will identify the desired custom header of which value should be retrieved. Basically the key is the custom header name without prefix and it is very important to understand that in order to have the value of the identified custom header bounded to the controller method parameter, the parameter name and the custom header name must be identical (excluding the prefix). So if you have a controller method public HttpResponseMessage GetFooByCriteria(Criteria criteria), the custom header full name has to be "X-criteria". Once the value is retrieved from the custom header, it is de-serialized into the desired object type (in this specific case JSON is used) and a new ValueProviderResult is returned. According to MSDN a ValueProviderResult represents the result of binding a value (such as from a form post or query string) to an action-method argument property, or to the argument itself. The last thing which has to be done is to register HeadeValueProvider into Web API pipeline by creating a custom ValueProviderFactory class. ValueProviderFactory represents a factory for creating value-provider objects.

using System.Web.Http.Controllers;
using System.Web.Http.ValueProviders;
 
namespace MyProject.Infrastructure.WebAPI
{
    public class HeaderValueProviderFactory<t> : ValueProviderFactory where T : class
    {
        public override IValueProvider GetValueProvider(HttpActionContext actionContext)
        {
            var headers = actionContext.ControllerContext.Request.Headers;
            return new HeaderValueProvider<t>(headers);
        }
    }
}

And finally here it is a short example of usage of HeaderValueProvider:

C#
public HttpResponseMessage GetFooByCriteria(
  [ValueProvider(typeof(HeaderValueProviderFactory<criteria>))] Criteria criteria)
{
.......
}

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)