[Dev Tip] Introduction to Web API Versioning


ASP.NET Web API Versioning based on namespace and custom HTTP headers.

Introduction

In this article lets discuss about Web API versioning and the need for the same.

Versioning of API is very much required if you are providing services for the public. It’s the only way we can make our API’s supporting forward and backward compatibility. By versioning you don’t effect the existing customers and break their applications which depends on your API.

The default Web API routing logic finds the controller by class name. The controller selection is handled internally by Web API through DefaultHttpControllerSelector which implementsIHttpControllerSelector.SelectController. We can’t really version Web API’s with this default controller selector but we can plug in our custom implementation for selecting the right version of our controller at run time. By implementing IHttpControllerSelector interface one can easily create a custom HTTP controller selector.

Let us see two different kinds of Web API versioning. You can decide on which ever is best and suits your needs. One can either go with namespace or HTTP custom header based versioning mechanism.

Background

Knowledgeable and have good understanding and development experience in Web API.
Some more interesting articles on Web API

1. Introduction to ASP.NET Web API
http://www.codeproject.com/Articles/549152/Introduction-to-ASP-NET-Web-API

2. ASP.NET WebAPI Hosting Techniques
http://www.codeproject.com/Articles/555923/ASP-NET-WebAPI-Hosting-Techniques

3. Implementing Custom DelegatingHandler in ASP.NET WebAPI
http://www.codeproject.com/Articles/557232/Implementing-Custom-DelegatingHandler-in-ASP-NET-W

4. Implementing Custom Media Formatters in ASP.NET WebAPI
http://www.codeproject.com/Articles/559378/Implementing-Custom-Media-Formatters-in-ASP-NET-We

Using the code

Below is the code snippet for getting the version number from HTTP custom header. Let us assume the default version of our Web API will always be empty.

private string GetVersionFromHTTPHeader(HttpRequestMessage request)
{
      if (request.Headers.Contains("version"))
      {
          var versionHeader = request.Headers.GetValues("version").FirstOrDefault();
          if (versionHeader != null)
          {
               return versionHeader;
          }
      }

      return string.Empty;
}

Below is the code snippet to get the version number based on the Mime Type. You can use either of the code to get the version number.

 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;
 }

Below is the code snippet for custom Web API controller selector which implementsIHttpControllerSelector.SelectController. From the below code you can notice that we are returning the controller descriptor based on the matched controller mappings or by version.

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 = GetVersionFromHTTPHeader(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;
}

So what next ?

Now that we have implemented custom controller selector. We will have to just replace the default one and make use of our custom WebAPI controller selector. This can be easily done by adding one line of code in WebApiConfig.

public static class WebApiConfig
{
        public static void Register(HttpConfiguration config)
        {
            // Web API configuration and services

            // Web API routes
            config.MapHttpAttributeRoutes();

            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );

            // Replace the controller configuration selector
            config.Services.Replace(typeof(IHttpControllerSelector), new CustomControllerSelector((config)));
        }
}


Below is the snapshot of the HTTP Request and Response. You can notice the “version” is being passed in HTTP Request header.

We shall see in brief about namespace based Web API versioning. This would be one of the most common technique one would make use and you will see most of the popular service providers do make use of these kind of techniques.

Below is the code snippet which essentially acts as a look up by returning the controller descriptor based on the namespace.

private Dictionary<string, HttpControllerDescriptor> InitializeControllerDictionary()
{
            var dictionary = new Dictionary<string,>(StringComparer.OrdinalIgnoreCase);

            // Create a lookup table where key is "namespace.controller". The value of "namespace" is the last
            // segment of the full namespace. For example:
            // MyApplication.Controllers.V1.ProductsController => "V1.Products"
            IAssembliesResolver assembliesResolver = _configuration.Services.GetAssembliesResolver();
            IHttpControllerTypeResolver controllersResolver = _configuration.Services.GetHttpControllerTypeResolver();

            ICollection<type> controllerTypes = controllersResolver.GetControllerTypes(assembliesResolver);

            foreach (Type t in controllerTypes)
            {
                var segments = t.Namespace.Split(Type.Delimiter);

                // For the dictionary key, strip "Controller" from the end of the type name.
                // This matches the behavior of DefaultHttpControllerSelector.
                var controllerName = t.Name.Remove(t.Name.Length - DefaultHttpControllerSelector.ControllerSuffix.Length);

                var key = String.Format(CultureInfo.InvariantCulture, "{0}.{1}", segments[segments.Length - 1], controllerName);

                // Check for duplicate keys.
                if (dictionary.Keys.Contains(key))
                {
                    _duplicates.Add(key);
                }
                else
                {
                    dictionary[key] = new HttpControllerDescriptor(_configuration, t.Name, t);  
                }
            }

            // Remove any duplicates from the dictionary, because these create ambiguous matches. 
            // For example, "Foo.V1.ProductsController" and "Bar.V1.ProductsController" both map to "v1.products".
            foreach (string s in _duplicates)
            {
                dictionary.Remove(s);
            }
            return dictionary;
}

Below is the code snippet for namespace based custom controller selector.

We will be getting the namespace and controller from the HTTP Request Message and then we will be finding the matching controller with in the dictionary. In order to find the match we will build the matching key as <namespace name>:<controller name>

public HttpControllerDescriptor SelectController(HttpRequestMessage request)
{
            IHttpRouteData routeData = request.GetRouteData();
            if (routeData == null)
            {
                throw new HttpResponseException(HttpStatusCode.NotFound);
            }

            // Get the namespace and controller variables from the route data.
            string namespaceName = GetRouteVariable<string>(routeData, “namespace”);
            if (namespaceName == null)
            {
                throw new HttpResponseException(HttpStatusCode.NotFound);
            }

            string controllerName = GetRouteVariable<string>(routeData, “controller”);
            if (controllerName == null)
            {
                throw new HttpResponseException(HttpStatusCode.NotFound);
            }

            // Find a matching controller.
            string key = String.Format(CultureInfo.InvariantCulture, "{0}.{1}", namespaceName, controllerName);

            HttpControllerDescriptor controllerDescriptor;
            if (_controllers.Value.TryGetValue(key, out controllerDescriptor))
            {
                return controllerDescriptor;
            }
            else if (_duplicates.Contains(key))
            {
                throw new HttpResponseException(
                    request.CreateErrorResponse(HttpStatusCode.InternalServerError,
                    "Multiple controllers were found that match this request."));
            }
            else
            {
                throw new HttpResponseException(HttpStatusCode.NotFound);
            }
}

// Get a value from the route data, if present.
private static T GetRouteVariable<t>(IHttpRouteData routeData, string name)
{
            object result = null;
            if (routeData.Values.TryGetValue(name, out result))
            {
                return (T)result;
            }
            return default(T);
}

Below is the code snippet on the usage of namespace based WebAPI Versioning. You can notice the HTTP Request has the version and controller names specified.

static void RunClient()
{
      HttpClient client = new HttpClient();
      client.BaseAddress = _baseAddress;

      using (HttpResponseMessage response = client.GetAsync("api/v1/values").Result)
      {
          response.EnsureSuccessStatusCode();
          string content = response.Content.ReadAsStringAsync().Result;
          Console.WriteLine("Version 1 response: '{0}'\n", content);
      }

      using (HttpResponseMessage response = client.GetAsync("api/v2/values").Result)
      {
          response.EnsureSuccessStatusCode();
          string content = response.Content.ReadAsStringAsync().Result;
          Console.WriteLine("Version 2 response: '{0}'\n", content);
      }
}

References

http://blogs.msdn.com/b/webdev/archive/2013/03/08/using-namespaces-to-version-web-apis.aspx

https://mathieu.fenniak.net/aint-nobody-got-time-for-that-api-versioning/

http://aspnet.codeplex.com/SourceControl/changeset/view/dd207952fa86#Samples/WebApi/NamespaceControllerSelector/

http://bitoftech.net/2013/12/16/asp-net-web-api-versioning-strategy/

REF: http://www.codeproject.com/Articles/741326/Introduction-to-Web-API-Versioning

Advertisements

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