Microsoft > CSharp >> Code Snippets Views : 1848
Rate This Article :

API Versioning in Web API

Introduction:

This article explains about the different types of API Versioning, its uses along with code samples on achieving the same. Versioning means, calling different Controller/Methods for the same URI based on the parameters coming the request URL.


Necessity of API Versioning:

For an example, you are developing a Service, which will be consumed by Client A & B. Now Client B wants additional functionalities on top of the existing service.

Without affecting Client A, we need to add additional functionalities in existing service with minimal changes. API versioning will help us to do the same.

Without Versioning, it is challengeable to provide the same URI (i.e. URL with same Controller & Action) to different vendors having different Business functionalities in it.


Types of API Versioning:

·         URI – This is straightforward approach, URI versioning uses routing to achieve api versioning.

·         Query string parameter – Query parameters will added to the URL, bases on the value in the query string appropriate action will trigger.

·         Custom Header parameter – Custom header key added to the request, so there will be no URI change required. Based on the value in the customer header key, appropriate action will trigger.

·         Accept Header parameter – Like customer header, custom value will passed in accept header in the request, no URI change required.

 

URI Versioning:

Step -1

Register the map routing in WebApiConfig for all the versions like below

  config.Routes.MapHttpRoute(

               name: "EmployeeV1",

               routeTemplate: "api/V1/{controller}/{action}/{id}",

               defaults: new { controller = "EmployeeV1", id = RouteParameter.Optional }

           );

 

            config.Routes.MapHttpRoute(

               name: "EmployeeV2",

               routeTemplate: "api/V2/{controller}/{action}/{id}",

               defaults: new { controller="EmployeeV2", id = RouteParameter.Optional }

           );

 

Above code registers two versions V1 and V2, based on the version value in the URL, system will redirect to appropriate controller.

Testing API Versioning

In the below screenshot, URI contains version information i.e V1, it executes Index action in EmployeeV1 controller.

API Versioning testing-1.png

In the below screenshot, URI contains version information i.e V2, it executes Index action in EmployeeV2 controller.

API Versioning testing-2.png


The only change in the above two example is version value in the URL (V1, V2).

 

Versioning through Query String:

Query string versioning is also more or less similar to URI versioning, URI get changed,

Step-1

Create custom controller selector class, write logic to read query string value and pick appropriate controller based on the query string value as shown below,

public class QueryStringSelectorController : DefaultHttpControllerSelector

    {

        HttpConfiguration _config;

        public QueryStringSelectorController(HttpConfiguration config) : base(config)

        {

            _config = config;

        }

 

        public override HttpControllerDescriptor SelectController(HttpRequestMessage request)

        {

            //returns all possible API Controllers

            var controllers = GetControllerMapping();

             //return the information about the route

            var routeData = request.GetRouteData();

            var versionQueryString = HttpUtility.ParseQueryString(request.RequestUri.Query);

            string apiVersion = "Home";

 

            //Get the query string value

            if (versionQueryString["version"] != null)

            {

                apiVersion = Convert.ToString(versionQueryString["version"]);

            }

 

            var controllerName = "";

        

            if (apiVersion == "V1")

            {

                controllerName = "EmployeeV1";

            }

            else

            {

                controllerName = "EmployeeV2";

            }

 

           

HttpControllerDescriptor controllerDescriptor;

             if (controllers.TryGetValue(controllerName, out controllerDescriptor))

            {

                return controllerDescriptor;

            }

             return null;

        }

    }

Step-2

Register custom action selector as default action selector in webApi.Config.cs

public static class WebApiConfig

    {

        public static void Register(HttpConfiguration config)

        {

            // Web API routes

            config.MapHttpAttributeRoutes();

 

            config.Routes.MapHttpRoute(

               name: "DefaultApi",

               routeTemplate: "api/{controller}/{action}/{id}",

               defaults: new { id = RouteParameter.Optional }

           );

 

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

        }

    }

In the below screenshot, URL has the query string parameter Version with Value “V1”, it executes the EmployeeV1 controller action.

API Versioning testing-3.png


In the below screenshot, URL has the query string parameter Version with Value “V2”, it executes the EmployeeV2 controller action.

API Versioning testing-4.png

API Versioning testing-5.png

Custom Header Parameter:

Custom header provides the facility to do versioning of API without changing the URI. With this approach, request content will inspected and based on the value of the version key, appropriate action fetched.

 

Step-1

Extending ApiControllerActionSelector to your new class as shown below

public class CustomActionSelector : ApiControllerActionSelector

{

}

 

Step-2

Register custom action selector as default action selector in webApi.Config.cs

public static class WebApiConfig

    {

        public static void Register(HttpConfiguration config)

        {

            // Web API routes

            config.MapHttpAttributeRoutes();

 

            config.Routes.MapHttpRoute(

               name: "DefaultApi",

               routeTemplate: "api/{controller}/{action}/{id}",

               defaults: new { id = RouteParameter.Optional }

           );

 

            config.Services.Replace(typeof(IHttpActionSelector), new CustomActionSelector());

        }

    }

 

Step-3

Override Select Action method in ApiControllerActionSelector to inspect request header and selected appropriate action.

 

public class CustomActionSelector : ApiControllerActionSelector

    {

        public override HttpActionDescriptor SelectAction(HttpControllerContext context)

        {

            var request = new HttpMessageContent(context.Request).HttpRequestMessage;

            var routeData = request.GetRouteData();

 

            string customHeaderForVersion = "X-Demo-Version";

            string apiVersion = "";

 

            if (request.Headers.Contains(customHeaderForVersion))

            {

                apiVersion = request.Headers.GetValues(customHeaderForVersion).FirstOrDefault();

            }

 

            var currentActionName = routeData.Values["action"].ToString();

 

            string actionName = currentActionName + "_" + apiVersion;

            var selectedAction = context.ControllerDescriptor.ControllerType.GetMethods().Where(p => p.Name == actionName).FirstOrDefault();

 

            if (selectedAction != null)

            {

                return new ReflectedHttpActionDescriptor(context.ControllerDescriptor, selectedAction);

            }

 

            return base.SelectAction(context);

        }

    }

 

Above method will called for each service request to select action. In our example custom version key is “X-Demo-Version”

HomeController having two actions Index_V1 and Index_V2, above action selector method will parse the request header, if the custom version key is available in the request then it will append the version value with the action name to select appropriate action to do versioning without changing service url.

public class HomeController : ApiController

    {

        [HttpGet]

        public string Index_V1()

        {

            return "Version One in Home controller";

        }

 

        [HttpGet]

        public string Index_V2(int id)

        {

            return "Version two Id= " + id.ToString() + " in Home controller";

        }

    }

 

Testing versioning

Add request header “X-Demo-Version” and test the versioning with providing value as V1 or V2 as shown below, system will fetch appropriate action based on value provided.

API Versioning testing-5.png


In some cases, we may want to change the controller based on custom header value to do api versioning, that be achieved with help of DefaultHttpControllerSelector, we will see below how to implement this approach.

Step-1

Extending ApiControllerActionSelector to your new class as shown below

public class CustomSelectorController: DefaultHttpControllerSelector

{

}

 

Step-2

Register custom controller selector as default controller selector in webApi.Config.cs

public static class WebApiConfig

    {

        public static void Register(HttpConfiguration config)

        {

            // Web API routes

            config.MapHttpAttributeRoutes();

 

            config.Routes.MapHttpRoute(

               name: "DefaultApi",

               routeTemplate: "api/{controller}/{action}/{id}",

               defaults: new { id = RouteParameter.Optional }

           );

 

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

 

            config.Services.Replace(typeof(IHttpActionSelector), new CustomActionSelector());

        }

    }

 

Step-3

Override Select Action method in SelectController to inspect request header and selected appropriate controller.

public override HttpControllerDescriptor SelectController(HttpRequestMessage request)

        {

           

            var controllers = GetControllerMapping();

           

          

            var routeData = request.GetRouteData();

 

            var controllerName = routeData.Values["controller"].ToString();

            string apiVersion = "1";

 

            string customHeaderForVersion = "X-Demo-Version";

            if (request.Headers.Contains(customHeaderForVersion))

            {

                apiVersion = request.Headers.GetValues(customHeaderForVersion).FirstOrDefault();

            }

 

            if (apiVersion == "Home")

            {

                controllerName = "Home";

            }

            else

            {

                controllerName = "Account";

            }


             HttpControllerDescriptor controllerDescriptor;

             if (controllers.TryGetValue(controllerName, out controllerDescriptor))

            {

                return controllerDescriptor;

            }

             return null;

        }

    }

 

Above method will inspect request header, if custom header available then it will check that value and fetch appropriate controller.


About Author
azadabul85
Total Posts 1
-
Comment this article
Name*
Email Address* (Will not be shown on this website.)
Comments*
Enter Image Text*
   
View All Comments
Comments not posted yet!! Please post your comments on this article.
  Privacy   Terms Of Use   Contact Us
© 2016 Developerin.Net. All rights reserved.
Trademarks and Article Images mentioned in this site may belongs to Microsoft and other respective trademark owners.
Articles, Tutorials and all other content offered here is for educational purpose only and its author copyrights.