How to do versioning with custom media type in C# ASP.NET WebAPI?


Media types allow an API to inform the client how to interpret the data in the payload. In the HTTP protocol, Media Types are specified with identifiers like text/html, application/json, and application/xml, which correspond to HTML, JSON, and XML respectively, the most common web formats. There are other more APIspecific Media Types as well, such as application/vnd.api+json.

Below are versions that needs to be send in media types.

application/vnd.demo.students.v1+json StudentsV1Controller
application/vnd.demo.students.v2+json StudentsV2Controller

Adding our own CustomControllerSelector will fix the above error.

CustomControllerSelector

Example

using System.Linq;
using System.Net.Http;
using System.Text.RegularExpressions;
using System.Web.Http;
using System.Web.Http.Controllers;
using System.Web.Http.Dispatcher;
namespace WebAPI.Custom{
   public class CustomControllerSelector : DefaultHttpControllerSelector{
      private HttpConfiguration _config;
      public CustomControllerSelector(HttpConfiguration config) : base(config){
         _config = config;
      }
      public override HttpControllerDescriptor SelectController(HttpRequestMessage
      request){
         var controllers = GetControllerMapping();
         var routeData = request.GetRouteData();
         var controllerName = routeData.Values["controller"].ToString();
         string versionNumber = "";
         string regex = @"application\/vnd\.demo\.([a-z]+)\.v(?<version>[0-9]+)\+([a-z]+)";
         var acceptHeader = request.Headers.Accept
            .Where(a => Regex.IsMatch(a.MediaType, regex,
            RegexOptions.IgnoreCase));
         if (acceptHeader.Any()){
            var match = Regex.Match(acceptHeader.First().MediaType,
            regex, RegexOptions.IgnoreCase);
            versionNumber = match.Groups["version"].Value;
         }
         HttpControllerDescriptor controllerDescriptor;
         if (versionNumber == "1"){
            controllerName = string.Concat(controllerName, "V1");
         }
         else if (versionNumber == "2"){
            controllerName = string.Concat(controllerName, "V2");
         }
         if (controllers.TryGetValue(controllerName, out controllerDescriptor)){
            return controllerDescriptor;
         }
         return null;
      }
   }
}

WebApi.Config.cs

Example

public static class WebApiConfig{
   public static void Register(HttpConfiguration config){
      config.Services.Replace(typeof(IHttpControllerSelector), new CustomControllerSelector(config));
      config.MapHttpAttributeRoutes();
      config.Routes.MapHttpRoute(
         name: "DefaultApi",
         routeTemplate: "api/{controller}/{id}",
         defaults: new { id = RouteParameter.Optional }
      );
   }
}

StudentV1Controller

Example

using DemoWebApplication.Models;
using System.Collections.Generic;
using System.Linq;
using System.Web.Http;
namespace DemoWebApplication.Controllers{
   public class StudentV1Controller : ApiController{
      List<StudentV1> students = new List<StudentV1>{
         new StudentV1{
            Id = 1,
            Name = "Mark"
         },
         new StudentV1{
            Id = 2,
            Name = "John"
         }
      };
      public IEnumerable<StudentV1> Get(){
         return students;
      }
      public StudentV1 Get(int id){
         var studentForId = students.FirstOrDefault(x => x.Id == id);
         return studentForId;
      }
   }
}

StudentV2Controller

Example

using DemoWebApplication.Models;
using System.Collections.Generic;
using System.Linq;
using System.Web.Http;
namespace DemoWebApplication.Controllers{
   public class StudentV2Controller : ApiController{
      List<StudentV2> students = new List<StudentV2>{
         new StudentV2{
            Id = 1,
            FirstName = "Roger",
            LastName = "Federer"
         },
         new StudentV2{
            Id = 2,
            FirstName = "Tom",
            LastName = "Bruce"
         }
      };
      public IEnumerable<StudentV2> Get(){
         return students;
      }
      public StudentV2 Get(int id){
         var studentForId = students.FirstOrDefault(x => x.Id == id);
         return studentForId;
      }
   }
}

Below outputs shows the results that we get from StudentV1 and StudentV2 controllers with versioning in custom media types.

So now if we want to get the same output in xml format using custom media type, add the below custom media type in webapiconfig.cs

Example

public static void Register(HttpConfiguration config){
   config.MapHttpAttributeRoutes();
   config.Services.Replace(typeof(IHttpControllerSelector), new
   CustomControllerSelector(config));
   config.Formatters.XmlFormatter.SupportedMediaTypes
      .Add(new MediaTypeHeaderValue("application/vnd.demo.student.v1+xml"));
   config.Formatters.XmlFormatter.SupportedMediaTypes
      .Add(new MediaTypeHeaderValue("application/vnd.demo.student.v2+xml"));
   config.Routes.MapHttpRoute(
      name: "DefaultApi",
      routeTemplate: "api/{controller}/{id}",
      defaults: new { id = RouteParameter.Optional }
   );
}

In the above example we could see that the output is in XML format as specified in the custom media type.

Updated on: 19-Aug-2020

192 Views

Kickstart Your Career

Get certified by completing the course

Get Started
Advertisements