A few days ago, I and my friends were developing an API using ASP.NET Core where from a GET
method we returned some data to our client app. We did pagination in the front end. This means that first, we sent all the data to the client and then did some data.length
operations on that to count the items. Then we decided that we should move the logic to 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 extreme control over our client app. We changed all the logic here, there, and everywhere and we were fine with it.
However, the 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 and meanwhile break 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 with that change.
If you are happy and you know it clap your hands 👏
The nightmares begin when you find out that your million-dollar mobile client is not working for that very simple (yet breaking) change and you are losing customers. 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 is 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 also can 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 3.*)
Install this package via NuGet: Microsoft.AspNetCore.Mvc.Versioning.
Now, configure it as a service in the ConfigureServices()
method of Startup.cs
, (duh! Like I didn't know that was coming)
Next, you have to decorate (i.e with ApiVersion
attribute) the controller on which you want support for API versioning (multiple versions). Likewise, you would also have to decorate your actions with a specific API version number (i.e. with MapToApiVersion
attribute):
Now to get the GET
action result you have to specify the API version 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. Something like,
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 the below:
You can now make a call to the API like, http://localhost:5000/api/values
with no harm made.
In the above configuration (default to version 1.0
), note that you will have to explicitly specify the version number 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:
There are three ways you can specify the API version. They can be set via:
In URL Path Versioning
, the way you pass the version number as a segment to your URL path. 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:
Note that the letter v
is not mandatory to add in front of the version number. It's just a convention.
You can configure your service to read the API version number from a specific Media Type
(By default it reads from the content-type
request header. You can configure your own media type). Configure your service like the below to activate media type visioning,
Now when you want to make an HTTP
request, just specify the API version as a part of the content-type
request header like below,
GET api/values HTTP/1.1
host: localhost
accept: text/plain;v=1.0
Response
--------
["value1", "value2"]
By the way, the CurrentImplementationApiVersionSelector
the option will take the most recent API version if there is no version defined as a part of the content-type
request header. In the following example, I didn't mention any version number so it's taking the most recent version among all the versions.
GET api/values HTTP/1.1
host: localhost
accept: text/plain;
Response
--------
{ data: ["value1", "value2"], count: 2 }
Last, you can also pass the version number using a header key. You have to configure the service like,
options => options.ApiVersionReader = new HeaderApiVersionReader("x-ms-version");
Now when you want to make a HTTP
request, just specify the API version as a part of the header key,
GET api/values HTTP/1.1
host: localhost
x-ms-version: 1.0
Response
--------
["value1", "value2"]
And that's not all. There are other cool features available and you can find those here in this official git repository of the project,
Comments