Posted on 5/18/2020 8:56:02 PM by Admin

Web API versioning using QueryString

This is the continuation of Web API Versioning Series, in the previous two articles we have learnt
  • why Versioning is required Read here and
  • Versioning using URI Read here

In this article we will learn how to implement versioning using QueryString. Till now we have two API Controllers :-
  1. SilverEmployeeController, which returns Employee's Name ,Id and Salary.
  2. GoldEmployeeController , which returns Employee's First Name ,Last Name, Id and Salary.

Our Client know nothing about the real implementation ,they just know that they are consuming EmployeeService and there exists two versions of the service. In order to consume the service they have to pass the version number too along with the URL , it's our duty to tell our client how they can pass the version number. There are many ways with which version number can be passed like:-
  1. In the Form of routed data .(versioning using URI)
  2. In the form of query string (versioning using query string)
  3. In the form of Custom Header (versioning using custom header)
  4. In the form of Accept Header (versioning using accept header)
  5. In the form of Custom Media Types (versioning using Custom media types).

Web API versioning using query string simply means the clients are going to specify the version number as the query string parameter like below

Versioning using query string
Passing version number as query string

So, our work is to tell web API to select appropriate controller based on the version number specified by the client as query string parameter.
  • If client passes v=1, then we have to tell web API to select SilverEmployeeService Controller.
  • If client passes v=2, then we have to tell web api to select GoldEmployeeService Controller.

In the previous case when client were passing version number as routed data value , telling web API to select appropriate controller based on the version number was easy through routing , but when version number is being passed as a query string parameter , routing is obviously not going to help , we can't define any route based on the query string value.

So ,we have to dive deep and try to understand how exactly web API selects controller ,when request is issued to a web api service, and is there any way to tweak the default implementation for controller selection.

How Web API Selects Controller

let us understand how a controller is selected when a request is issued to the following URI http://localhost:1234/api/EmployeeService/3
  • In Web API, there is a class called DefaultHttpControllerSelector.
  • This class has a method called SelectController() that selects the controller based on the information it has in the URI.

Now ,in the URI we have following information
  1. Name of the Controller - EmloyeeService
  2. Value of the Id parameter- 3.

So from the URI, the SelectController() method takes the name of the controller in this case "EmployeeService" and finds " EmployeeServiceController" and returns it. This is the default implementation that works behind the scene.

But ,what we want ?

for the URL http://localhost:1234/EmployeeService?v=1 ,we want the web API to select the SilverEmployeeController , but we know the web will try to find EmployeeServiceController as it gets EmployeeSrvice as the Controller name from the URI , and since no such controller exists in our application so error will be thrown.

Now ,what else we could do to tell the web api to select controller based on the querystring parameter ( version number )

The DefaultHttpControllerSelector class allows us to extend the SelectController() method as it is marked as virtual. So, in order to tell the web api to select controller based on the query string value, we have to extend the SelectController() method and give it our own custom implementation.
Step 1.Add a folder in the solution and name it Custom.
step 2.Add a class file and name it CustomControllerSelector.
Step 3.Inherit this class from DefaultHttpControllerSelector and override the SelectController() method ,like below.


  using System;
  using System.Collections.Generic;
  using System.Linq;
  using System.Net.Http;
  using System.Text.RegularExpressions;
  using System.Web;
  using System.Web.Http;
  using System.Web.Http.Controllers;
  using System.Web.Http.Dispatcher;

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

            public override HttpControllerDescriptor
                SelectController(HttpRequestMessage request)
            {
                // Get all the available Web API controllers
                var controllers = GetControllerMapping();
                // Get the controller name and parameter values from the request URI
                var routeData = request.GetRouteData();

                // Get the controller name from route data.
                // The name of the controller in our case is "EmployeeService"
                var controllerName = routeData.Values["controller"].ToString();

                // Default version number to 1
                string versionNumber = "1";
                var versionQueryString = HttpUtility.ParseQueryString(request.RequestUri.Query);
                

                if (versionQueryString["v"] != null)
                {
                    versionNumber = versionQueryString["v"];
                }

                if (versionNumber == "1")
                {
                    // if version number is 1, then prepend "Silver" to the controller name.
                    // So at this point the, controller name will become SilverEmployeeService
                    controllerName = "Silver"+controllerName;
                }
                else
                {
                    // if version number is 2, then prepend "Gold" to the controller name.
                    // So at this point the, controller name will GoldEmployeeService
                    controllerName ="Gold"+controllerName;
                }

                HttpControllerDescriptor controllerDescriptor;
                if (controllers.TryGetValue(controllerName, out controllerDescriptor))
                {
                    return controllerDescriptor;
                }

                return null;
            }
        }
    
    }

The implementation is simple and straight forward, we are getting version number from querystring and accordingly changing the controller Name.

Step 4.Tell the web API to use CustomControllerSelector instead of the Default one ,by replacing the IHttpControllerSelector with CustomControllerSelector instance in WebApiConfig.CS file which resides under the App_Start folder.


   namespace MyFirstAPIProject
    {
    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            
          config.Services.Replace(typeof(IHttpControllerSelector),
          new CustomControllerSelector(config));

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

          
        }
    }

   
  }

Now , run the application and fire two get request on the behalf of two different client like below and test the results.

Getting appropriate result based on version number
Versioning using query string

As you can see ,for the query string v=2 we are getting list of GoldEmployee back ,it means our custom controller selector is successfully working and versioning is successful.