API versioning in ASP.Net Core
While supporting Web API projects that have been around for some time, we often face the problem of the logic of controller methods becoming obsolete and the need to change it in accordance with new requirements. But, as a rule, at the time of such a need, there are already a certain number of services that use the current implementation of our APIs and do not need to be updated. Moreover, such services can easily break when the APIs they use change.
To solve this kind of problem, ASP.Net Core has an API versioning mechanism – when controllers and their methods can exist simultaneously in different versions. In this case, those services that are satisfied with the existing state of the APIs they use can continue to use certain versions of these APIs, and for services that require updating the controller logic, we can create new parallel versions, and all these versions can work in our project at the same time .
In Visual Studio, create a new ASP.NET Core Web API project:
Add the NuGet package to the new project: Microsoft.AspNetCore.Mvc.Versioning
To do this, in SolutionExplorer (Solution Explorer), right-click on the name of the working project and select Manage NuGet Packages… (Managing Nuget Packages).
Next, go to the leftmost tab Browse, and in the search bar enter the name of the NuGet package to be installed.
In the left window, select the package we need, and in the right, click the button Install.
Add the line “services.AddApiVersioning();” to the ConfigureServices method of the Startup.cs class:
public void ConfigureServices(IServiceCollection services)
{
// другой код
services.AddControllers();
services.AddApiVersioning();
// другой код
}
In the ‘Controllers’ folder, create a new folder “V2”.
Add a new API controller to the created folder:
And we call it WeatherForecastController, just like the controller created by default.
In the new controller, we add the logic of an existing controller, and a little
we change it. We also add an attribute to the controller [ApiVersion(“2.0”)]
and in the Route attribute we change the route:
[Route("api/v{version:apiVersion}/[controller]")]
[ApiController]
[ApiVersion("2.0")]
public class WeatherForecastController : ControllerBase
{
private static readonly string[] Summaries = new[]
{
"Сильный мороз", "Мороз", "Холодно", "Прохладно", "Свежо", "Тепло", "Духота", "Жара", "Сильная жара"
};
[HttpGet]
public IEnumerable<WeatherForecast> Get()
{
var rng = new Random();
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = rng.Next(-40, 40),
Summary = Summaries[rng.Next(Summaries.Length)]
})
.ToArray();
}
}
The route of the new controller will now be: “/api/v2/WeatherForecast”.
Add the attribute to the controller automatically created with the project [ApiVersion(“1.0”)]:
[ApiController]
[Route("[controller]")]
[ApiVersion("1.0")]
public class WeatherForecastController : ControllerBase
{
// созданная по умолчанию логика
}
Thus, we have two versions of essentially the same controller – 1 and 2.
The call route of the first controller remains the same, but now we must tell the router the version we need.
There are several ways to pass the controller version:
in request parameters: https://localhost:44335/WeatherForecast?api-version=1.0
in HTTP request headers. To do this, you need to modify the AddApiVersioning method in the ConfigureServices method of the Startup.cs class:
services.AddApiVersioning(config =>
{
config.ApiVersionReader = new HeaderApiVersionReader("api-version");
});
and add the appropriate header to the request:
Let’s call the method of the first controller. To do this, we will use the method of passing the version through query parameters.
Let’s run our project, and in a browser or API tool (Postman, Insomnia, etc.) enter Url: “https://localhost:44335/WeatherForecast?api-version=1.0”. Of course, the port must be specified according to your application.
We get the result:
[
{
"date":"2022-02-06T00:22:54.6248567+06:00",
"temperatureC":-13,
"temperatureF":9,
"summary":"Scorching"
},
{
"date":"2022-02-07T00:22:54.6261864+06:00",
"temperatureC":24,
"temperatureF":75,
"summary":"Freezing"
},
{
"date":"2022-02-08T00:22:54.6261919+06:00",
"temperatureC":-12,
"temperatureF":11,
"summary":"Freezing"
},
{
"date":"2022-02-09T00:22:54.6261927+06:00",
"temperatureC":40,
"temperatureF":103,
"summary":"Sweltering"
},
{
"date":"2022-02-10T00:22:54.6261931+06:00",
"temperatureC":27,
"temperatureF":80,
"summary":"Balmy"
}
]
8. Let’s call the method of the second controller. To do this, we will use the method of passing the version to the request Url, since we set it using the attribute [Route(“api/v{version:apiVersion}/[controller]”)]and must now follow it.
Let’s run our project, and in a browser or an API tool (Postman, Insomnia, etc.) enter the Url: “https://localhost:44335/api/v2/WeatherForecast“.
We get the result:
[
{
"date":"2022-02-06T00:32:43.5572436+06:00",
"temperatureC":27,
"temperatureF":80,
"summary":"Жара"
},
{
"date":"2022-02-07T00:32:43.5577678+06:00",
"temperatureC":14,
"temperatureF":57,
"summary":"Сильная жара"
},
{
"date":"2022-02-08T00:32:43.5577702+06:00",
"temperatureC":15,
"temperatureF":58,
"summary":"Тепло"
},
{
"date":"2022-02-09T00:32:43.5577706+06:00",
"temperatureC":0,
"temperatureF":32,
"summary":"Духота"
},
{
"date":"2022-02-10T00:32:43.5577707+06:00",
"temperatureC":-14,
"temperatureF":7,
"summary":"Тепло"
}
]
Calling the first controller in its original route without specifying a version – https://localhost:44335/WeatherForecast
Suppose the first controller is already used by several other projects (services) in its original form, as it was before it was added to the versioning project, and no one is going to refactor these projects and change the Url for the call. They are still accessing our API via the route “https://localhost:44335/WeatherForecast“.
In this case, we set the default version, and if the version is not specified in the request, then the request is directed to the specified version.
To do this, let’s change the AddApiVersioning method in the ConfigureServices method of the Startup.cs class:
services.AddApiVersioning(config =>
{
config.DefaultApiVersion = new ApiVersion(1, 0);
config.AssumeDefaultVersionWhenUnspecified = true;
});
In this case, all requests that do not specify a version will be directed to the controller of the first version. Now we can call the first controller in its original route, it will work.
We can mark the first version as obsolete by slightly changing the version attribute: [ApiVersion(“1.0”, Deprecated = true)]
Using the MapToApiVersion attribute, we have the ability to manage versions at the method level:
[MapToApiVersion("3.0")]
[HttpGet]
public int DoSomething()
{
// логика метода
}