Using versioning in your ASP.NET Core API

Why should you care about versioning your API? Well, writing a web API with ASP.NET Core is easy. Getting it in production also. But what happens when you have your API live, clients are out there consuming it, and the need arises to change something? This is, when you should have thought about versioning your ASP.NET Core web API. This article shows how to version a web API written using ASP.NET Core. It shows different approaches and explains the differences between them.

When to version?

First of all, we should think a bit about when we need to up our version number of our API. When our endpoints offer JSON, under normal circumstances it is not a problem when we add additional properties to the objects we send. So adding something to the response usually does not break clients consuming our API. At least it should not, and clients should be able to handle additional properties.

However, when we remove (or rename) properties I our responses, this will break clients that rely on the information and the old name. So doing this is a breaking change and should introduce a new version.

The same goes for changes to the addresses of the endpoints and the objects that are sent to them in the request body. When we add required additional parameters to our methods (URLs or values), clients don’t magically learn to send that. We could avoid introducing a new API version though, if we still accept the old requests and answer them as we did before. In this case we only change behavior when the new arguments are send from newer clients.

Different ways you can implement versioning

There are multiple ways we could access different versions of our web api. Probably one of the most common ones is dedicating a part of the path to the version, like so: This, however, requires you to take care of versioning at the very beginning of your project and introduce the version to the URL from day 1. Changing the URL to introduce the version later on would be a breaking change. This would allow your clients to manage the API version they work with at a single point: The base path to your API.

Another possibility is, to add the version to the query parameter: While you can specifically default to v1 when the query parameter is not set, this somehow makes querying your API a bit tedious, as you have to take care to add the parameter to every query.

The third way to specifically address an API version is a more REST-like way and uses common HTTP content negotiation between the client and the server. In this case, a client should always specifically request the version it is going to access. It sends the version along in the accept header of the API request. Example: accept: application/json;v=2.0 If no version is specified, the server should default to the latest implementation. Like the first approach, this requires to think about versioning from the very beginning, as a client that does not send the version will break when the API changes.

Then there is a fourth way: Custom headers. You can send the requested version in a custom header field, and decide by that. Here you are more free to chose what your APIs default behavior should be in case the header is missing, e.g. falling back to v1.

Caveats and upgrade paths from non-versioned to versioned APIs

If you planned for versioning beforehand, you already chose one of the possibilities, and you can skip to the next section.

If you didn’t plan for versioning from the very beginning, you most likely simply call a path on your API (e.g. without any version information. This allows introducing a version part in the path as well as the query string for new clients only. You can add the versioned endpoints to the API under the new path with the version part in it, or with the additional query parameter. The old clients continue to call the old v1 endpoints. If you want to go for one of the header-based options, you should fall back to v1 in case of missing headers. Unless you want to explicitly work against common practices, you shouldn’t go with the accept header. Speaking semantically correct http with your API would default to the latest version, breaking old clients.

Also the header-based options may run into problems when used in certain networks. I encountered strange WAFs (Web Application Firewall). These removed any custom headers, as well as filtered header values if it didn’t knew about them (like the version in the accept header). Depending on the hosting environment, there’s a chance you can’t use these header-based approaches. Luckily, environments that are restricted that harshly aren’t too common.

Implement versioning

In ASP.NET Core, there is already infrastructure available for that. First of all, install the following NuGet package into your API project: Microsoft.AspNetCore.Mvc.Versioning.

Then, for a new (or a small existing) project, add the following code to your Startup class in the ConfigureServices method:

On all controllers you already have in your new project, add the following attribute: [ApiVersion( "1.0" )]
Also add this to all new controllers you are going to add in the future, with the correct corresponding version.

If you have an existing project and don’t want to add the [ApiVersion("1.0")] attribute to all your existing controllers, add this instead:
services.AddApiVersioning(options => {
options.AssumeDefaultVersionWhenUnspecified = true;

This will make the versioning system assume that a controller that does not specify a version implements v1.0 of your API.

Now we need a versioned API call. Let’s assume we did that in a new API project based on the ASP.NET Core API project template, with the known ValuesController, and amend this values controller like so:
[ApiVersion("1.0")] // Add this
[ApiVersion("2.0")] // Add this
public class ValuesController : ControllerBase
// GET api/values
// This is used vor API version 1
public ActionResult<IEnumerable> Get()
return Get("v1");

// GET api/values
public ActionResult<IEnumerable> Get(string param = "v2")
return new string[] { "value1", "value2", param };

// here the rest of the class

In fact, this is already enough to make versioning work. By default, the API versioning will listen on a query parameter. From this moment on, you can add ?api-version=x.y to access any version your API provides. If you request a version that your API does not (yet) support, the system will automatically respond with an HTTP 400 code (Bad Request), and return a JSON object that states that the API does not implement the requested version. This looks like this:
"error": {
"code": "UnsupportedApiVersion",
"message": "The HTTP resource that matches the request URI 'https://localhost:44363/api/values?api-version=3.0' does not support the API version '3.0'.",
"innerError": null

You can find the code for this so far in my explanatory GitHub repository in the branch webapi-versioning-queryparameter.

To use the path to select the version it is sufficient to add another route to the controller: [Route("api/v{version:apiVersion}/[controller]")]. This will leave the original (unversioned) route in tact, and add a second versioned route to the controller. This will allow both ways, query parameter and path, to work simultaneously. If no version is provided by either query parameter or path, it will default to version 1. You can see this in another branch in the sample repository.

To be able to use the REST-like accept header, slightly other changes are required. Instead of adding the second route the controller, we change the configuration in our Startup class to this:
services.AddApiVersioning(options =>
options.AssumeDefaultVersionWhenUnspecified = true;
options.ApiVersionReader = new MediaTypeApiVersionReader();
options.ApiVersionSelector = new CurrentImplementationApiVersionSelector(options); // optional, but recommended

This will change the system to look in the accept header for the version. Please make sure, that the accept header does not state only the version, as this won’t work. A working value would be accept: application/json;v=2.0. The ApiVersionSelector should be changed too, to make this API behave more REST-like and default to the latest version instead of v1. You can see this in the third branch in the example repo.

I mentioned another way using a custom header. This can be done by replacing the MediaTypeApiVersionReader in the aforementioned constellation with an instance of HeaderApiVersionReader and specifying the custom header name you want to use:
// will read from the 'my-api-version' header
options.ApiVersionReader = new HeaderApiVersionReader("my-api-version");


Introducing versioning to your ASP.NET Core API isn’t that difficult. You need to decide on one of the possible ways to address a specific version of your API, and implement that. Probably the most common approaches are path and accept header. Query string and a custom header are also possible, but not that commonly used. The actual implementation is only a matter of adding a NuGet package, configuration, and adding some attributes on your API controllers.

How to run the Babun shell in Cmder


I want to show you how you can the Babun shell in Cmder.

From my previous blog posts you know I am a fan of Cmder on Windows. It gives me a powerful shell as a replacement of the normal cmd.exe, and it also lets me run Bash and Zsh in the Windows Subsystem for Linux (WSL).

That said, Cmder does not provide a full featured *nix shell, but an extended Windows cmd prompt. And while Bash (or Zsh) on the WSL provide with an full featured Linux shell, it has some drawbacks as it really IS a Linux environment. For example, when you install certain Node packages in your project like PhantomJS, they will bring the Linux version and executables, and you can’t run them from your normal Windows environment.

Babun is a tool, that can bridge this gap. Continue reading “How to run the Babun shell in Cmder”

English keyboard layout hints for German developers

The German keyboard layout is great. It has everything you need, including all of the umlauts, the ß and also the µ. For programming, however, the German keyboard layout is 💩: The normal braces are on 8 and 9 with shift state, just off by one from the English ones at 9 and 0, but the square and curly braces are available only on Alt Gr combos left and right from that. Also the forward and backward slashes are Shift and Alt Gr combos. You can imagine that this slows you down.

So, to be more efficient when writing code, I tried and trained myself to use an English keyboard layout. This is, what I learned. Continue reading “English keyboard layout hints for German developers”

A little design update for my blog

You may notice it, this blog has got a small design update.

The old theme was fine, but it was very… white with just a little bit of black. I decided to look out for another theme that adds a little bit of color, but still focuses on readability and cleanness.

After checking out a lot of themes, testing them and navigating around to get a gasp of the overall look & feel of different themes, I found one that pleases me and hopefully you, too: I decided to go with the official WordPress Twenty Seventeen theme and just give the color scheme a little personal touch.

If you are in the mood, please leave some feedback on how you like my new design. 😉

My developers toolbelt 2016

I cought a tweet a few days ago asking for your developers toolbelt, specifically on windows. And I gave a very short answer and mentioned I would blog about this:

So, this is a more elaborate answer to the developers toolbelt question. My default windows developer installation contains the following:

Continue reading “My developers toolbelt 2016”

Running Windows 10 Ubuntu Bash in Cmder

“Can you run Bash in Cmder?” – In the comments of my last post (install and run (Oh-my-) zsh on Bash on Ubuntu on Windows), I was asked whether it would be possible to run the Bash (or Zsh) also in Cmder. First I thought it was not possible, but then I got curious. After digging in a bit more it turned out that it IS, in fact, possible. And it’s not difficult too.

So, since I figured out how it works, I also want to show you how you can run the Windows 10 Ubuntu Bash (and/or Zsh) in Cmder.
Continue reading “Running Windows 10 Ubuntu Bash in Cmder”