Lorsque vous utilisez la gestion des versions dans asp.net core avec swashbuckle for swagger, vous ne pouvez pas avoir par défaut plusieurs méthodes dans le même contrôleur comme celle-ci :

[Route("v{version:apiVersion}/[controller]")]
[ApiVersion("1")]
[ApiVersion("2")]
[ApiController]
public class TestController : ControllerBase
{
    /// <summary>
    /// test ver 1
    /// </summary>
    /// <returns></returns>
    [HttpGet]
    [MapToApiVersion("1")]
    [ProducesResponseType(StatusCodes.Status200OK)]
    public string Get()
    {
        return "Version 1";
    }

    /// <summary>
    /// test ver 2
    /// </summary>
    /// <returns></returns>
    [HttpGet]
    [MapToApiVersion("2")]
    [ProducesResponseType(StatusCodes.Status200OK)]
    public string Get2()
    {
        return "Version 2";
    }
}

Par défaut, vous aurez un message d'erreur

Swashbuckle.AspNetCore.SwaggerGen.SwaggerGeneratorException : combinaison méthode/chemin en conflit « GET v{version}/Test » pour les actions

  • ...Contrôleurs.TestController.Get,...TestController.Get2

Parce que swagger-ui a reconnu les deux méthodes par le même chemin

localhost/v{version:apiVersion}/Test

La solution basique de cape et d'épée consiste à avoir deux contrôleurs distincts pour chaque version de méthode. C'est une mauvaise solution pour mon cas d'utilisation.

1
110mat110 8 oct. 2020 à 16:11

1 réponse

Meilleure réponse

La solution est la vérification manuelle de la version dans le setter swagger. Dans mon cas, j'ai également ajouté la liste des versions prises en charge.

Vous devez donc ajouter à Startup.cs ceci :

private readonly string[] supportedVersions = new string[] { "1", "2" };

public void ConfigureServices(IServiceCollection services)
{
...
services.AddApiVersioning();
services.AddSwaggerGen(c =>
{
            
    c.DocInclusionPredicate((docName, apiDesc) =>
    {
        var metadata = apiDesc.ActionDescriptor.EndpointMetadata;
        var apiVersionModel = metadata.Where(x => x.GetType() == typeof(ApiVersionAttribute)).Cast<ApiVersionAttribute>();
        var supportedVersions = metadata.Where(x => x.GetType() == typeof(MapToApiVersionAttribute)).Cast<MapToApiVersionAttribute>();
        if (apiVersionModel.IsNullOrEmpty() || supportedVersions.IsNullOrEmpty())
        {
            return false;
        }

        var apiVersions = apiVersionModel.SelectMany(x => x.Versions.Select(y => y.MajorVersion));
        var versions = supportedVersions.SelectMany(x => x.Versions.Select(y => y.MajorVersion));

        return apiVersions.Any(v => $"v{v.ToString()}" == docName) && versions.Any(v => $"v{v.ToString()}" == docName);
    });
    c.DocumentFilter<ReplaceVersionWithExactValueInPathFilter>();
    c.OperationFilter<RemoveVersionParameterFilter>();
    c.EnableAnnotations();
    supportedVersions.ForEach(sv =>
    {
        c.SwaggerDoc($"v{sv}", new OpenApiInfo()
        {
            Version = $"v{sv}",
            Title = $"API v{ sv }",
            Description = "My web api",
        });
    });
    var baseDirectory = AppContext.BaseDirectory;
    var xmlFiles = Directory.EnumerateFiles(baseDirectory, "*.xml");

    xmlFiles.ForEach(x => c.IncludeXmlComments(x));
});
...
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
            app.UseSwaggerUI(c =>
        {
            supportedVersions.ForEach(sv =>
            {
                c.SwaggerEndpoint($"/swagger/v{sv}/swagger.json", $"My api v{sv}");
            });
        });
    }

Et séparer le fichier de deux méthodes pour éviter d'afficher la version en tant que paramètre dans l'interface utilisateur. Sinon, vous verrez des points de terminaison avec le paramètre de version au lieu de la version elle-même.

public class RemoveVersionParameterFilter : IOperationFilter
{
    public void Apply(OpenApiOperation operation, OperationFilterContext context)
    {
        var versionParameter = operation.Parameters.FirstOrDefault(p => p.Name == "version");
        if (versionParameter == default(OpenApiParameter))  return;
        var parameters = operation.Parameters.ToList();
        operation.Parameters.Remove(versionParameter);
    }
}

public class ReplaceVersionWithExactValueInPathFilter : IDocumentFilter
{
    public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
    {
        var paths = new OpenApiPaths();
        foreach (var path in swaggerDoc.Paths)
        {
            
            paths.Add(path.Key.Replace("v{version}", swaggerDoc.Info.Version), path.Value);
        }
        swaggerDoc.Paths = paths;
    }
}
1
110mat110 8 oct. 2020 à 13:17