ASP.NET Core API Versioning in Simple Words (Update 1.2.0)

Few days ago, me and my friends developed an API using ASP.NET Core where from the GET method we returned some data to our client app. We did pagination in the front end. Means that first we sent all the data to the client and then did some data.length operation on that to get the items count for applying pagination logic. Then decided that we should do the logic in the back-end (server-side pagination) because it will decrease the payload of the HTTP request. In our case it wasn't any problem since we had an extreme control over our client app (it's singular). We changed all the logic here and there both in the client and server for that. And we were fine with it.

However, possibility is you will have additional clients and only a single source of truth (API). Introducing a breaking change in one API can support one client meanwhile breaking others. For example, let's say your mobile team is on vacation and your web team is working their asses off for that server-side pagination stuff. To support the web team, you made a simple change (who cares!) in the API. You and your web team both are happy (if you are happy and you know it clap your hands) with that change. The nightmares begin when you find out that your million-dollar mobile client is not working for that very simple (yet breaking) change and people are uninstalling it. More nightmares come later when you find out that neither you are a mobile app developer nor you have access to its source code. Now you only have the option of downgrading both your API and web app. But the team who made the web app are now on vacation too. I'll stop there now cause too many nightmares going around here.

Maybe (not maybe. It is!) API versioning is a good practice in that case. With API versioning, you can not only be safe against those breaking changes but can also support those. It's a win-win situation for everyone.

Let's see how to configure API versioning in ASP.NET Core.

Note: I'm using an empty ASP.NET Core Web API project (.NET Core 1.1)

Download .NET Core

Install this package via NuGet: Microsoft.AspNetCore.Mvc.Versioning. Now, configure it as a service (i.e. services.AddApiVersioning()) in the ConfigureServices() method of Startup.cs (duh! Like I didn't know that was coming):

 public void ConfigureServices(IServiceCollection services)
 {
     services.AddMvc();
     services.AddApiVersioning();
 }

Following app.UseApiVersioning() middleware configuration is only needed when you are using version 1.1.1 of Microsoft.AspNetCore.Mvc.Versioning

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    /* garbage code removed */
    app.UseMvc();
    app.UseApiVersioning();
}

Next, you have to decorate (i.e ApiVersion) the controller on which you want support for API versioning (multiple versions). Likewise, you would also have to decorate (i.e. MapToApiVersion) your actions with a specific API version number:

[ApiVersion("1.0")]
[Route("api/[controller]")]
public class ValuesController : Controller
{
    // GET api/values
    [MapToApiVersion("1.0")]
    [HttpGet]
    public IEnumerable<string> Get()
    {
        return Json(new string[] { "value1", "value2" });
    }
}

Now to get the GET action result you have to specify the API version (which it responds to) of it. For the time being, use the query string versioning way. In this way, you will specify the API version directly in the query string. Like this: http://localhost:5000/api/values?api-version=1.0

If you are adding API versioning to your existing API project, you can tell ASP.NET Core to treat the undercoated controllers and actions to have version 1.0 by default. To do that, configure the AddApiVersioning() service like below:

services.AddApiVersioning(options => options.AssumeDefaultVersionWhenUnspecified = true);

You can now make a call to the API like http://localhost:5000/api/values with no harms made.

In the above configuration (default to version 1.0), note that you will have to explicitly define the ApiVersion("1.0") when you want to use other versions along with it. However, you don't have to decorate the action in this case. Below is an example of the situation:

[ApiVersion("2.0")]
[Route("api/[controller]")]
public class ValuesController : Controller
{
    [HttpGet]
    public IActionResult Get()
    {
        return Json(new string[] { "value1", "value2" });
    }

    [HttpGet, MapToApiVersion("2.0")]
    public IActionResult GetWithTotal()
    {
        var data = new string[] { "value1", "value2" };
        var total = data.Length;
        return Json(new { data = data, total = total });
    }
}

There are three ways you can specify the API version. They can be set via:

  • Querystring (already discussed)
  • URL Path Segment
  • Media types

In URL Path Versioning way you pass the version number as a segment of your URL path. Like this for example: http://localhost:5000/api/v1/values. By the way you have to modify your Route attribute to accommodate version segment in it like below:

[ApiVersion("1.0")]
[Route("api/v{version:apiVersion}/[controller]")]
public class ValuesController : Controller
{
    [HttpGet, MapToApiVersion("1.0")]
    public IActionResult Get()
    {
        return Json(new string[] { "value1", "value2" });
    }
}

Note that the letter v is not mandatory to add in front of the version number. It's just a convention.

Last of all, you can configure your service to read the API version number from a specific Media Type (By default it reads from the content-type media type by default. you can configure your own media type). Configure your service like the below to activate media type visioning:

public void ConfigureServices(IServiceCollection services)
{
        // Add framework services.
    services.AddMvc();
    services.AddApiVersioning(options =>
    {
        options.ApiVersionReader = new MediaTypeApiVersionReader();
        options.AssumeDefaultVersionWhenUnspecified = true;
        options.ApiVersionSelector = new CurrentImplementationApiVersionSelector(options);
    });
}

Now when you make an HTTP request specify the API version as a part of the content-type media type in the header section like below (content-type: application/json;v=2.0):

By the way, the CurrentImplementationApiVersionSelector option will take the most recent API version if there is no version defined as a part of the content-type media type. In the following example, I didn't mention any version number so it's taking the most recent version among all the versions.

And that's not all. There are other cool features available and you can find those here in Microsft's ASP.NET API Versioning git repository:

https://github.com/Microsoft/aspnet-api-versioning/wiki