ASP.NET Core Web API versioning

Simple way to setup versioning in ASP.NET Core WebAPI

  • Share

When it comes to WebAPI many project overlook the versioning aspect. The whole concept of WebAPI is to serve multiple types of clients which can be based on different platforms, run on different devices etc. Since many clients can depend on the service it is crucial to maintain support for old clients since not all clients can be ready at the same time to switch to new version.

Since .NET Core is a new thing and a lot of people are trying to switch to this platform which not only allows to run ASP.NET WepAPI on multiple OS platforms, but it is a lot lighter than full .NET framework, I decide to write this article to help implement versioning of API methods as simple as possible without any additional NuGet packages. So let's start.

First we agree on the convention of the controllers structure and every controller needs to be in it's separate version folder. Without following this, this approach is not applicable, so if you cannot maintain this structure, you should look for a bit more different approach.

Controller structure should be as following:

Api Core Ver

.NET Core concept relies heavily on the injection, so everything you want to use in you application needs to be injected in the startup.

We need to add new convention for the MVC service when adding to application in Startup class.

using Microsoft.AspNetCore.Mvc.ApplicationModels;
using System;
using System.Collections.Generic;
using System.Linq;
namespace WebApplication1
{
    public class VersioningConventions : IApplicationModelConvention
    {
        public void Apply(ApplicationModel application)
        {
            foreach (var controller in application.Controllers)
            {
                //Check if route attribute is alredy defined
                var hasRoute = controller.Selectors.Any(selector => selector.AttributeRouteModel != null);
                if (hasRoute)
                {
                    continue;
                }

                //Get the version as last part of namespace
                var version = controller.ControllerType.Namespace.Split('.').LastOrDefault();

                controller.Selectors[0].AttributeRouteModel = new AttributeRouteModel()
                {
                    Template = string.Format("api/{0}/{1}", version, controller.ControllerName)
                };
            }
        }
    }
}

    

From the code of our new convention class you can see that in case there is routing attribute in the controller, this convention will not be applied. Therefore our controllers from the both versions folder need to be without any routing attributes, otherwise convention will not be applied to them.

By default, Visual Studio adds Route attribute to the controller, so if you wan to test this approach you need to remove them. Your controllers need to be without the Route attributes as following:

Version v1

namespace WebApplication1.Controllers.v1
{
    public class ValuesController : Controller
    {
        // GET api/values
        [HttpGet]
        public IEnumerable<string> Get()
        {
            return new string[] { "value1", "value2" };
        }

        // GET api/values/5
        [HttpGet("{id}")]
        public string Get(int id)
        {
            return "value";
        }
    }
}
    

Version v2

namespace WebApplication1.Controllers.v2
{
    public class ValuesController : Controller
    {
        // GET api/values
        [HttpGet]
        public IEnumerable<DateTime> Get()
        {
            return new DateTime[] { DateTime.Now, DateTime.Now.AddDays(1) };
        }

        // GET api/values/5
        [HttpGet("{id}")]
        public DateTime Get(int id)
        {
            return DateTime.Now;
        }
    }
}
    

Visual Studio compiler will not complain that you have two controllers of the same name because they are in the separate namespaces, but if you try to run the WebAPI project you will get 404 for the URL /api/values.

Now we need to modify ConfigureServices method in Startup class to use our convention class:

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            // Add framework services.
            services.AddMvc(options => options.Conventions.Add(new VersioningConventions()));
        }
    

If you run the project now you will see that you have different results for the urls /api/v1/values and /api/v2/values.

You might want to configure your launchSettings.json file to point to your actual version you are debugging and avoid 404 every time you hit F5

{
  "iisSettings": {
    "windowsAuthentication": false,
    "anonymousAuthentication": true,
    "iisExpress": {
      "applicationUrl": "http://localhost:53762/",
      "sslPort": 0
    }
  },
  "profiles": {
    "IIS Express": {
      "commandName": "IISExpress",
      "launchBrowser": true,
      "launchUrl": "api/v1/values",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    },
    "WebApplication1": {
      "commandName": "Project",
      "launchBrowser": true,
      "launchUrl": "api/v1/values",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      },
      "applicationUrl": "http://localhost:53763"
    }
  }
}

    
  • Share

Disclaimer

Purpose of the code contained in snippets or available for download in this article is solely for learning and demo purposes. Author will not be held responsible for any failure or damages caused due to any other usage.

Comments for this article

comments powered by Disqus