Web API Versioning using URI
First, I will cover WebApi versioning using an URL in this section before proceeding. I’d like to share one of the possible situations when you may need this. So let’s dive into it in a practical way.
You might have a situation in which you already have a running WebApi hosted on a server and plenty of end users are getting its benefits. For an example, here the current API is returning a response in JSON format as shown below:
Down the line, the customer may desire to have a few more things without messing with the existing one. After considering a new requirement, the client needs a few more fields from the same API to maintain their policies at the business level, so now we can consider a new version of the API with a few more fields like City and Gender in employee in response as depicted below:
To do this we’ll set a new route for the new version of the WebApi Version number in the URI, considering the most common way of versioning so we can consume a resource by calling http://localhost:57888/api/v1/Employees/FetchEmployeeById/1 (the client wants to use version 1 of the API) or http://localhost:57888/api/v2/employeesv2/FetchEmployeeById/1 (the client wants to use version 2 of the API).
To implement this we need to add two new routes inside the Register method in the “WebApiConfig” class as in the code below:
Notice in the code shown previously there are two new routes and mapped each route with its corresponding controller. In other words, the Route named “Version1? is by default mapped to the controller “Employees? and the Route named “Version2Api” is mapped to the Controller EmployeesV2.
Assume we want to add a new version, V3, of the existing WebApi. Then we need to add a new route to WebApiConfig.cs and so on.
Before proceeding I’d like to share how WebApi selects an appropriate controller from the current request. There is a class DefaultHttpControllerSelector that has the method SelectController, that method has the parameter HttpRequestMessage to maintain the information of route data including controller name defined in the class WebApiConfig. Based on this information it fetches the controller/classes collection using reflection derived from the ApiController base class.
The following is a code snippet for CustomControllerSelector:
- public class CustomControllerSelector : DefaultHttpControllerSelector
- {
- private HttpConfiguration _config;
- public CustomControllerSelector(HttpConfiguration config) : base(config)
- {
- _config = config;
- }
- public override HttpControllerDescriptor SelectController(HttpRequestMessage request)
- {
- try
- {
- var controllers = GetControllerMapping();
- var routeData = request.GetRouteData();
- var controllerName = routeData.Values["controller"].ToString();
- HttpControllerDescriptor controllerDescriptor;
- if (controllers.TryGetValue(controllerName, out controllerDescriptor))
- {
- return controllerDescriptor;
- }
- return null;
- }
- catch (Exception ex)
- {
- throw ex;
- }
- }
- }
- config.Services.Replace(typeof(IHttpControllerSelector), new CustomControllerSelector((config)));
Kindly find the code below for the EmployeesController class and Employee Model class that represent version v1 of the WebApi as shown below:
- public class EmployeesController : ApiController
- {
- public static IList<Employee> listEmp = new List<Employee>()
- {
- new Employee()
- {
- ID =001, FirstName="Sachin", LastName="Kalia"
- },
- new Employee()
- {
- ID =002, FirstName="Dhnanjay" ,LastName="Kumar"
- },
- new Employee()
- {
- ID =003, FirstName="Ravish", LastName="Sindhwani"
- },
- new Employee()
- {
- ID =004, FirstName="Rahul" ,LastName="Saxena"
- },
- };
- [AcceptVerbs("GET")]
- public Employee FetchEmployeeById(int id)
- {
- return listEmp.First(e => e.ID == id);
- }
- }
- public class Employee
- {
- public int ID { get; set; }
- public string FirstName { get; set; }
- public string LastName { get; set; }
- }
To test this please press F5 and run you WebApi.
To test this we need to issue a GET request using the Web Proxy tool Fiddler as in the image below. Kindly copy and paste the following URL that specifies version v1:
http://localhost:57888/api/v1/Employees/FetchEmployeeById/1
Code snippet for Version v2
Kindly find the code below for the EmployeesV2Controller class and EmployeeV2 Model class that specifies version v2 of the WebApi:
- public class EmployeesV2Controller : ApiController
- {
- public static IList<EmployeeV2> listEmp = new List<EmployeeV2>()
- {
- new EmployeeV2()
- {
- ID =001, FirstName="Sachin", LastName="Kalia",City="Noida",Gender="Male"
- },
- new EmployeeV2()
- {
- ID =002, FirstName="Dhnanjay" ,LastName="Kumar", City="Gurgaon",Gender="Male"
- },
- new EmployeeV2()
- {
- ID =003, FirstName="Ravish", LastName="Sindhwani", City="indianapolis",Gender="Male"
- },
- new EmployeeV2()
- {
- ID =004, FirstName="Neeraj", LastName="Arora", City="San Francisco",Gender="Male"
- },
- new EmployeeV2()
- {
- ID =005, FirstName="Rahul" ,LastName="Saxena", City="Hydrabad",Gender="Male"
- },
- new EmployeeV2()
- {
- ID =006, FirstName="Anshu" ,LastName="Agarwal", City="Noida",Gender="Female"
- },
- };
- [AcceptVerbs("GET")]
- public EmployeeV2 FetchEmployeeById(int id)
- {
- var coll = listEmp.FirstOrDefault(e => e.ID == id);
- return coll;
- }
- }
- public class EmployeeV2
- {
- public int ID { get; set; }
- public string FirstName { get; set; }
- public string LastName { get; set; }
- public string City { get; set; }
- public string Gender { get; set; }
- }
Press F5 and run you WebApi.
To test this we need to issue a GET request using the Web Proxy tool Fiddler as in the image below. Kindly copy and paste the following URL that specifies version v2 into the Web Proxy tool Fiddler:
http://localhost:57888/api/v2/employeesv2/FetchEmployeeById/1.
Web API Versioning using QueryString parameter
WebApi versioning with query string parameter is a simple way to this, because everything is dependent on the query string parameter only.
We’ll specify the defined version of the WebApi in the request parameter. Using that parameter the SelectController class will identify which controller I need to call in order to respond to the client.
Added a new method into the existing CustomControllerSelector and made small amendments in the SelectController class as shown below in a code snippet.
- public override HttpControllerDescriptor SelectController(HttpRequestMessage request)
- {
- try
- {
- var controllers = GetControllerMapping();
- var routeData = request.GetRouteData();
- var controllerName = routeData.Values["controller"].ToString();
- HttpControllerDescriptor controllerDescriptor;
- string versionNum= GetVersionFromQueryString(request);
- if (versionNum == "v1")
- {
- if (controllers.TryGetValue(controllerName, out controllerDescriptor))
- {
- return controllerDescriptor;
- }
- }
- else
- {
- controllerName= string.Concat(controllerName,"V2");
- if (controllers.TryGetValue(controllerName, out controllerDescriptor))
- {
- return controllerDescriptor;
- }
- }
- return null;
- }
- catch (Exception ex)
- {
- throw ex;
- }
- }
- /// <summary>
- /// Method to Get Query String Values from URL to get the version number
- /// </summary>
- /// <param name="request">HttpRequestMessage: Current Request made through Browser or Fiddler</param>
- /// <returns>Version Number</returns>
- private string GetVersionFromQueryString(HttpRequestMessage request)
- {
- var versionStr = HttpUtility.ParseQueryString(request.RequestUri.Query);
- if (versionStr[0] != null)
- {
- return versionStr[0];
- }
- return "V1";
- }
Press F5 and run you WebApi.
Kindly paste the following URL into Fiddler to verify the working behavior.
http://localhost:57888/api/employees/FetchEmployeeById/2?V2
Kindly have a look at the image shown below:
In the same way you can try with:
http://localhost:57888/api/employees/FetchEmployeeById/1?V1
Web API Versioning using Custom Header parameter
So far we’ve seen WebApi with the two techniques URL and Query String. The next WebApi versioning techniques with CustomHeader parameter is an easy way to do versioning. For this example we’ve used customHeader Name “Version-Num” that we must send into the current request.
We’ll specify the desired version of the WebApi in the CustomHeader request. Using that parameter the SelectController class will identify the controller I need to call in order to respond to the client.
Added a new method into the existing CustomControllerSelector and made small amendments in the SelectController class as shown below in a code snippet.
- /// <summary>
- /// Method to Get Header Values.
- /// </summary>
- /// <param name="request">HttpRequestMessage: Current Request made through Browser or Fiddler
- </param>
- /// <returns>Version Number</returns>
- private string GetVersionFromHeader(HttpRequestMessage request)
- {
- const string HEADER_NAME = "Version-Num";
- if (request.Headers.Contains(HEADER_NAME))
- {
- var versionHeader = request.Headers.GetValues(HEADER_NAME).FirstOrDefault();
- if (versionHeader != null)
- {
- return versionHeader;
- }
- }
- return "V1";
- }
Web API Versioning using Accept Header parameter
So far we’ve seen WebApi with the three techniques URL, Query String and CustomHeader.
The next WebApi versioning technique uses an AcceptHeader parameter that is an easy way to do WebApi versioning. For this example we’ve used an Accept Header value Accept: application/json or "Accept: application/xml, that we need to send into the current request.
For this approach we’ve added a new method in the existing CustomControllerSelector and made small amendments in the SelectController class as shown below in a code snippet.
- /// <summary>
- /// Method to Get Accept Header Values.
- /// </summary>
- /// <param name="request">HttpRequestMessage: Current Request made through Browser or Fiddler</param>
- /// <returns>Version Number</returns>
- private string GetVersionFromAcceptHeader(HttpRequestMessage request)
- {
- var acceptHeader = request.Headers.Accept;
- foreach (var mime in acceptHeader)
- {
- if (mime.MediaType == "application/json")
- {
- return "V2";
- }
- else if (mime.MediaType == "application/xml")
- {
- return "V1";
- }
- else { return "V1"; }
- }
- return "V1";
- }
Now if we replace Accept: application/xml with application/json then it should respond from WebApi version V2. Kindly have a look at the image shown below:
No comments:
Post a Comment