Web API Versioning (using Header) – Part 2

Introduction

In this post, I am going to discuss Web API versioning using custom Header. We are already discussed versioning using URI i.e. namespace based versioning.

How versioning through Header?

To perform versioning through HTTP Header, we set a Header in request to differentiate API resources and on the server we have to check the Header and execute the controller that is specified by Header value.

What Header is to be used?

Have a look on following Header

Accept: text/plain; charset=utf-8;version=1
Accept header parameters define:

Media type (e.g. application/json)
Character encoding (e.g. charset=utf-8)
Other parameter: vesion=1

I am using this approach for this post.

 

Start Coding

First, create a new Web Application with Web API template and create two controllers.

StudentController.cs

namespace ApiVersionH.Controllers
{
    public class StudentController : ApiController
    {
        public Students Get()
        {
            //Creating student as response
            return new Students()
            {
                Id = 100,
                Name = "Student_V1_Name",
                Message = "In V1"
            };
        }
    }
}

 

StudentV2Controller.cs

namespace ApiVersionH.Controllers
{
    public class StudentV2Controller : ApiController
    {
        public Students Get()
        {
            //Creating student as response
            return new Students()
            {
                Id = 1,
                Name = "Student_V2_Name",
                Message = "In V2"
            };
        }
    }
}

 

Get the version number from MIME parameter

To select controller based on Header parameter, we have to create a custom controller selector class which derive fromDefaultHttpControllerSelector.

CustomControllerSelector.cs

namespace ApiVersionH.Routing
{
    public class CustomControllerSelector : DefaultHttpControllerSelector
    {
        private HttpConfiguration _config;
        public CustomControllerSelector(HttpConfiguration config)
            : base (config)
        {
            _config = config;
        }

        public override HttpControllerDescriptor SelectController(HttpRequestMessage request)
        {
            var controllers = GetControllerMapping(); 
            var routeData = request.GetRouteData();
            var controllerName = routeData.Values["controller"].ToString();
            HttpControllerDescriptor controllerDescriptor;
            if (controllers.TryGetValue(controllerName, out controllerDescriptor))
            {
                var version = GetVersionFromAcceptHeaderVersion(request);
                if (!string.IsNullOrEmpty(version))
                {
                    var versionedControllerName = string.Concat(controllerName, "V", version);
                    HttpControllerDescriptor versionedControllerDescriptor;
                    if (controllers.TryGetValue(versionedControllerName, out versionedControllerDescriptor))
                    {
                        return versionedControllerDescriptor;
                    }
                }
                return controllerDescriptor;
            }
            return null;
        }

        private string GetVersionFromAcceptHeaderVersion(HttpRequestMessage request)
        {
            var acceptHeader = request.Headers.Accept;
            foreach (var mime in acceptHeader)
            {
                if (mime.MediaType == "application/json" || mime.MediaType == "text/html")
                {
                    var version = mime.Parameters
                                    .Where(v => v.Name.Equals("version", StringComparison.OrdinalIgnoreCase))
                                    .FirstOrDefault();
                    if (version != null)
                    {
                        return version.Value;
                    }
                    return string.Empty;
                }
            }
            return string.Empty;
        }
       
    }
}

In this class, we are modifying the default controller selector behaviour by overriding SelectController method. In this, we find the version number from Accept Header parameter and then concatenate It with V, because our version V2 Controller method end withV2.

One more configuration

We have to register our CustomControllerSelector  to ASP .NET web API. So some modification in WebAPIConfig:

WebApiConfig.cs

namespace ApiVersionH
{
    public static class WebApiConfig
    {
    public static void Register(HttpConfiguration config)
        {
            config.MapHttpAttributeRoutes();
            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );
            // Replace the default controller selector
            config.Services.Replace(typeof(IHttpControllerSelector), new CustomControllerSelector((config)));
        }
    }
}

We are simply replacing the default controller selector with our custom controller selector.

Time to execute

Build and run the solution with following parameters

Request to version 1:

GET /api/student HTTP/1.1
User-Agent: Fiddler
VERSION: 1
Host: localhost:59475
accept: application/json;version=1

Response we are getting is

{"Id":100,"Name":"Student_V1_Name","Message":"In V1"}

Request to version 2:

GET /api/student HTTP/1.1
User-Agent: Fiddler
Host: localhost:59475
accept: application/json;version=2

Response we are getting is

{"Id":1,"Name":"Student_V2_Name","Message":"In V2"}

That’s work perfect! 

Argument around the web for versioning approaches

One day I was looking for current practices used for version of REST API.on web, I find many argument for using versioning through URI or Header, and even combinations of these and different approaches. Some are here taken from stackoverflow only for information:

dcerecedo said:

Semmantically using version number in header seems better. But its far much more practical using the
URL: less error prone, best debuged, readily seen by developers, easily modifiable in rest testing clients.

Yoav Shapira said:

We found it practical and useful to put the version in the URL. It makes it easy to tell what you’re using at a glance. We do alias /foo to /foo/(latest versions) for ease of use, shorter / cleaner URLs, etc,

benvoliot Said:

Using a version number in the URL should not be considered a bad practice when the underlying
implementation changes. “When the interface to a service changes in a non-backwards-compatible way, in reality an entirely new service has been created…From the client’s perspective, a service is no more than an interface and some non-functional qualities…if the interface to a service changes in a non-backwards compatible way, it no longer represents an instance of the original service, but is rather a completely new service.”

A hope

I hope that you will have better understanding of API versioning through Header and handling the HTTP Header. I would like to hear valuable feedback or question from you. It encourage me to write more quality article.
Happy Coding, Versioning 🙂

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s