Gestire le versioni di una asp.net Core WebAPI

Una delle più importanti ragioni percui le WebAPI hanno preso largamente piede negli ultimi anni è la possibilità di disaccoppiare fortemente la parte di rappresentazione da quella del layer dati/applicativo. Questo forte disaccoppiamento necessita però che cambi radicali alle WebAPI non vadano a discapito di chi le consuma: se cambio un API dovrei essere sicuro che una volta cambiata tutto ciò che prima funzionava continui a funzionare nella stessa maniera altrimenti potrei potenzialmente “rompere” delle funzionalità di applicazioni che consumano queste API. La maniera migliore per farla è quella di procedere ad un versionamento delle API, ma prima di farlo occorre capirsi sul quando è necessario creare una nuova versione delle API e quando no. Vi lascio questo link [1] che è ricco di spunti ed è ciò su cui ho basato questo post. Riassumendo le casistiche sarebbero più o meno le seguenti:

  • Rimuovere o rinominare API o i suoi parametri
  • Cambiamenti significativi nel comportamento dell’API
  • Cambiamenti al response contract
  • Cambiamenti ai codici di errore

Per prima cosa dobbiamo definire le versioni all’interno del Program.cs. In questo caso definiamo anche la version 1 come quella di default.

builder.Services.AddApiVersioning(options =>
{
    options.DefaultApiVersion = new ApiVersion(1);
    options.ReportApiVersions = true;
    options.AssumeDefaultVersionWhenUnspecified = true;
    options.ApiVersionReader = ApiVersionReader.Combine(
        new UrlSegmentApiVersionReader(),
        new HeaderApiVersionReader("X-Api-Version"));
}).AddApiExplorer(options =>
{
    options.GroupNameFormat = "'v'V";
    options.SubstituteApiVersionInUrl = true;
});

Successivamente occorre decorare il controller con le Versioni supportate e con il conseguente path dinamico basato sulla versione

[ApiVersion(1)]
[ApiVersion(2)]
[Route("api/v{v:apiVersion}/[controller]")]
public class InfoAPIController : ControllerBase
{

A questo punto devono essere decorati appositamente tutti i metodi che hanno più versioni con lo stesso Http Get name ma differente nome C#

[MapToApiVersion(1)]
[HttpGet(Name = "GetInfo")] //, Authorize]
public string GetV1(string name)
{
    ...

[MapToApiVersion(2)]
[HttpGet(Name = "GetInfo")] //, Authorize]
public string GetV2(string name)
{

Fatto ciò dovremmo quindi essere in grado di usufruire versioni diverse in base al path utilizzato. In realtà, come spiegato per bene nel post sotto, le modalità potrebbero essere differenti ma io opto per un verisoning basato sull’url.

Tutto molto bello ma tutto ciò non basta a visualizzare due differenti versini in Swagger. Per farlo occorrono un altro paio di accortezze che ho scoperto in un altro post [2]. La prima è che vanno configurate le versioni visibili all’interno della configurazione di swagger (nel mio caso sono due):

builder.Services.AddSwaggerGen(options =>
{
    options.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme
    {
        In = ParameterLocation.Header,
        Name = "authorization",
        Type = SecuritySchemeType.ApiKey
    });

    options.OperationFilter<SecurityRequirementsOperationFilter>();

    options.SwaggerDoc("v1", new OpenApiInfo { Title = "Xin Web API", Version = "v1"});
    options.SwaggerDoc("v2", new OpenApiInfo { Title = "Xin Web API", Version = "v2" });
});

Infine nel SwaggerUI vanno registrati i path delle versioni, ma invece di farlo uno ad uno consiglio di utilizzare l’approccio descritto qui [3]

    app.UseSwaggerUI(options =>
    {
        var descriptions = app.DescribeApiVersions();

        // Build a swagger endpoint for each discovered API version
        foreach (var description in descriptions)
        {
            var url = $"/swagger/{description.GroupName}/swagger.json";
            var name = description.GroupName.ToUpperInvariant();
            options.SwaggerEndpoint(url, name);
        }
    });

Attenzione che i due passi sopra sono fondamentali se volete visualizzare correttamente nella drop down di swagger netrambe le versioni e switchare tra di esse i due punti sopra sono fondamentali.

Swagger con le due versioni selezionabili.

[1] https://www.milanjovanovic.tech/blog/api-versioning-in-aspnetcore

[2] https://dev.to/sardarmudassaralikhan/swagger-implementation-in-aspnet-core-web-api-5a5a

[3] https://mohsen.es/api-versioning-and-swagger-in-asp-net-core-7-0-fe45f67d8419