API Versioning In ASP.NET Core
At some point in time in every API’s lifecycle, upgrading it to a new version may be inevitable. With this comes a couple of thinking points to consider before proceeding and perhaps implementing a new version that might contain breaking changes. This is where API version can come in and help you along the way.
In the past, we all had our various ways of doing this, from setting it on the query string, using http request headers or attributes or using routes for the different versions. This all fine and acceptable then, but with .NET Core, you can add versioning to your API in just a couple of lines of code.
To get started, install the Microsoft.AspNetCore.Mvc.Versioning package to your API project.
Add this following code to your Starup.cs ConfigureServices method to add the versioning.
Above we are adding versioning for out API while also configuring it. Here's what we are doing:
- ReportApiVersions - Setting this to true reports the current version of your API in the response header
- DefaultApiVersion - This is the current version of your API
- AssumeDefaultVersionWhenUnspecified – Setting this to true will have your API assume a version (Default) if no version is supplied the client requesting it.
Once we have have our versioning service set up, we need to create versioned endpoints of out API. We will use the simple example below:
What we have above are 2 controllers, one for Version 1 and another for Version 2. If we run our API and go to api/Values/ endpoint, Version 1.0 will be invoked because we did not specify which version of our API we would like to use and also, we configured the versioning service to assume our default version when the client does not provide a version to use.
Next, we will look at techniques to specify the version.
There are 3 popular ways of specifying the version to use when call your API.
The first is to use a query string parameter in your requests. For example, you could use the call below to call version 2 of your API:
The second option, if you’re like me and don’t like adding stuff onto your query strings, is to use URL-based versioning. In this case we add the versioning by changing the route, see below:
Using the second approach, we can call version 2 of our API using the call below:
Lastly, we can use HTTP header-based versioning. This option is appealing if changing the URL is not an option for you at all, on the query string or by setting the route as we did in the previous two examples. To enable this, the version reader config needs to be added to the configuration we added in the ConfigureServices method we did above.
Once the API Version reader is enabled, you can specify the API version while calling this particular API by adding the api-version header to your requests.
When you have a rather large application, with many controllers it does become tedious to use an attribute on each controller to indicate the version. To avert this, we could create what we call in the .NET world conventions. This allows you to control the version of your controllers from a single place in your application.
Adding conventions as we did above mean we don’t have to add the attribute on every controller.
You will most definitely have a couple of endpoints in your API that are not necessarily have to conform to the current version of your API. We have two options to get around this problem.
The first is to make the endpoint neutral. For example, the example below shows the ping endpoint in our API. It is very unlikely that this method be updated and moved to the latest version of our API. To overcome this, we can add the ApiVersionNeutral attribute.
Secondly, we might have a controller with various methods that might not be getting upgraded to the newest version. We might have 10 methods on this controller and only to two of these methods are being updated and moved to version 3 of our API. When this happens, we don't want to update the version on the controller level. For this, we can use the MapToApiVersion attribute. Example below:
Using this attribute, we can still have Version 2 of our controller operate the way it always has, but we also updated the method and mapped it specifically for version 3 of our API.
As time goes by and older versions of your API are no longer being maintained, you would want to notify your consumers of this. To do this we could add the Deprecated attribute to your controllers.
This will not remove the endpoints from your API, instead it will return the list of deprecated versions in the response header.
When to version an API varies. If you currently have an API out in the wild and have customers consuming it and you have a couple of changes that only affect your responses, chances are, these are not breaking changes and that your clients are probably able to handle these additions properties to your objects. But let’s say for instance you are updating a POST methods and a couple of addition properties are required. This is a breaking change and I would advise you to maybe bump up the version number.
Hope this helps.