How to version your Web API

Take advantage of the various ways to version your Web API to make it more flexible and adaptable to changes while keeping the functionality intact

How to version your Web API
Matejmo / Getty Images

You should always version your Web API while at the same time keeping as much of the same URI as possible. Imagine a situation where you have a Web API that is up and running in production and is being consumed by users. Now suppose you need more functionality in the Web API yet must keep the existing functionality intact. You may have a few users who still need the old API, while others will need a version with new or extended features. This is exactly where Web API versioning comes to the rescue.

You can version your Web API in one of the following ways:

  1. Use URLs: Version information is specified in the URL as a query string.
  2. Use Custom Request Headers: Version information for your controller is specified in the request header sans the need for any changes in the URL.
  3. Use Accept Headers: Accept headers generally define the media type and character encodings. You can pass version information for your Web API via accept headers without having to change the URL.

Versioning Web API using URLs

Consider the following Web API controllers, which have been named AuthorsV1Controller and AuthorsV2Controller respectively.

public class AuthorsV1Controller : ApiController
    {
        [HttpGet]
        public IEnumerable<string> GetAuthors()
        {
          return new string[] { "Joydip Kanjilal", "Gerben Wierda" };
        }
    }
public class AuthorsV2Controller : ApiController
    {
        [HttpGet]
        public IEnumerable<string> GetAuthors()
        {
            return new string[] { "Joydip Kanjilal, INDIA", "Gerben Wierda, Netherlands" };
        }
    }

To simplify this illustration, I’ve incorporated a method named GetAuthors() in each controller. While GetAuthors() in AuthorsV1Controller returns only the author names, GetAuthors() in AuthorsV2Controller (the new version) returns the author names along with the names of the countries in which the authors reside.

The following code snippet shows how the two controllers use the Register method of the WebApiConfig class.

config.Routes.MapHttpRoute(
                name: "IDGWebAPIV1",
                routeTemplate: "api/v1/{controller}/{id}",
                defaults: new { controller= "AuthorsV1Controller", action="GetAuthors", id = RouteParameter.Optional }
            );
config.Routes.MapHttpRoute(
                name: "IDGWebAPIV2",
                routeTemplate: "api/v2/{controller}/{id}",
                defaults: new { controller = "AuthorsV2Controller", action = "GetAuthors", id = RouteParameter.Optional }
            );

You can now invoke the Web API method GetAuthors using the following URL.

http://localhost/IDGWebAPI/api/v1/Authors/GetAuthors

Versioning Web API using the request header

You can also implement Web API versioning using the request header. To achieve this, you need to implement a custom class that extends the DefaultHttpControllerSelector class, then override SelectController in your custom class. Note that the DefaultHttpControllerSelector class implements the IHttpControllerSelector interface. SelectController calls GetControllerName internally and accepts an instance of HttpRequestMessage as a parameter.

The following code snippet illustrates how you can retrieve version information from the request header.

private string GetControllerVersionFromRequestHeader(HttpRequestMessage request)
        {
            var acceptHeader = request.Headers.Accept;
            const string headerName = "Version";
            string controllerVersion = string.Empty;
            if (request.Headers.Contains(headerName))
            {
                controllerVersion = "V"+request.Headers.GetValues(headerName).First();               
            }
            return controllerVersion;
        }

Versioning Web API using the accept header

The following method shows how you can retrieve version information for your Web API from the accept header. The method checks the MIME type and returns the version information appropriately. If the media type is not application/json, the default version is returned as V1.

private string GetControllerVersionFromAcceptHeader(HttpRequestMessage request)
        {
            var acceptHeader = request.Headers.Accept;
            string controllerVersion = string.Empty;
            foreach (var mime in acceptHeader)
            {
                if (mime.MediaType.Equals("application/json"))
                {
                    NameValueHeaderValue version = mime.Parameters.FirstOrDefault(v => v.Name.Equals("Version", StringComparison.OrdinalIgnoreCase));
                    controllerVersion = "V" + version.Value.ToString();
                    return controllerVersion;
                }
            }
            return "V1";
        }

You can invoke your Web API from Fiddler by passing the accept header as shown below.

Accept: application/json; charset=utf-8;version=2

The following code listing illustrates how you can override SelectController to select a controller dynamically. Note how GetControllerVersionFromRequestHeader has been used. If you would like to retrieve the controller version from the accept header, you should leverage GetControllerVersionFromAcceptHeader instead.

public override HttpControllerDescriptor SelectController(HttpRequestMessage request)
        {
            try
            {
                string controllerName = base.GetControllerName(request);
                var controllers = GetControllerMapping();
                var routeData = request.GetRouteData();
                string controllerVersion = GetControllerVersionFromRequestHeader(request);             
                controllerName = String.Format("{0}{1}", controllerName, controllerVersion);
                HttpControllerDescriptor controllerDescriptor;
                if (!controllers.TryGetValue(controllerName, out controllerDescriptor))
                {
                    string message = "No HTTP resource was found that matches the specified request URI {0}";
                    throw new HttpResponseException(request.CreateErrorResponse(System.Net.HttpStatusCode.NotFound, String.Format(message, request.RequestUri)));
                }
                return controllerDescriptor;
            }
            catch (Exception ex)
            {
                throw new HttpResponseException(request.CreateErrorResponse(System.Net.HttpStatusCode.NotFound, String.Format(ex.Message, request.RequestUri)));
            }
        }

You should add the following line in the Register method of the WebApiConfig class to provide support for controller selection at runtime.

config.Services.Replace(typeof(IHttpControllerSelector), new IDGControllerSelector((config)));

You can now use Fiddler to test your Web API — use the composer tab of Fiddler and provide the URL and version information as appropriate. If you want version 2 of your Web API controller to be invoked, you should specify Version: 2 when composing the request header information in the Composer tab in Fiddler.

Copyright © 2016 IDG Communications, Inc.