web-dev-qa-db-fra.com

Swashbuckle/Swagger sur .NET Core 2.1 a cessé de fonctionner depuis la mise à niveau

J'ai une application .NET Core 2.0, qui utilise Swashbuckle/Swagger pour générer la documentation de l'API. Lorsque nous étions sur la version 2.1.0-preview, Swagger fonctionnait bien. Nous avons ensuite procédé à la mise à niveau vers la version 2.1.0 et le SDK 2.1.300. Nous n'avons pas remarqué exactement quand les choses se sont cassées, mais maintenant nos documents Swagger ne se chargent pas. Voici ce que nous voyons:  Failed to load API definition Errors Fetch error undefined /swagger/v1/swagger.json

Le projet a une référence à Swashbuckle.AspNetCore version 2.5.0. Le code correspondant dans Startup.cs est ci-dessous. Dans ConfigureServices():

services.AddSwaggerGen(swaggerOptions =>
{
    // Register a swagger doc
    swaggerOptions.SwaggerDoc("v1", new Info
    {
        // Optional descriptive info that will be included in the Swagger output
        Contact = new Contact
        {
            Name = "LightSail",
            Url = "https://myurl.com/"
        },
        Description = "A description of the API can go here",
        Title = "My API",
        Version = "v1"
    });

    // Xml file to get comment information from
    swaggerOptions.IncludeXmlComments("App_Data/Api.xml");
});

Et dans Configure():

app.UseSwagger();

app.UseSwaggerUI(swaggerUiOptions => swaggerUiOptions.SwaggerEndpoint("/swagger/v1/swagger.json", "My API v1"));

J'ai trouvé beaucoup d'autres questions similaires, l'une d'entre elles suggérant qu'il pourrait y avoir des points finaux en double; J'ai essayé d'ajouter un appel à .ResolveConflictingEndpoints() mais cela ne faisait aucune différence. J'ai effectué une recherche dans mes dossiers de projet et il n'y a pas de fichier appelé swagger.json, alors je suppose que c'est le problème. 

Des idées pourquoi cela ne fonctionne pas, ou comment résoudre ce problème?

3
Shaul Behr

Cela indique généralement des contrôleurs/actions que Swashbuckle ne prend pas en charge pour une raison ou une autre. 

Il est prévu que votre projet ne contienne pas de fichier swagger.json. Swashbuckle crée et gère cela de manière dynamique à l'aide des API ApiExplorer d'ASP.NET Core. Ce qui se passe probablement ici est que Swashbuckle est incapable de générer Swagger.json et que, par conséquent, l'interface utilisateur ne s'affiche pas.

Comme HelderSepu l'a dit, il est difficile de savoir exactement ce qui a causé l'échec. Le meilleur moyen de déboguer consiste probablement à supprimer la moitié de vos contrôleurs (il suffit de déplacer les fichiers dans un emplacement temporaire) et à vérifier si les problèmes persistent. Vous saurez alors quelle moitié de vos contrôleurs contient l’action problématique. Vous pouvez effectuer une «recherche binaire» en supprimant les contrôleurs (puis les actions) jusqu'à ce que vous sachiez quelle méthode d'action empêche Swashbuckle de générer Swagger.json. Une fois que vous le savez, il devrait être évident qu'il s'agisse d'un problème dans votre code ou d'un problème devant être classé dans le Swashbuckle repo .

Par exemple, Swashbuckle ne semble pas prendre en charge les génériques ouverts. Par conséquent, un attribut de type de réponse tel que [ResponseType(typeof(IEnumerable<>))] peut être à l'origine de ce type de comportement. Cela pourrait également être un problème avec des itinéraires ambigus ou quelque chose comme ça qui déclenche Swashbuckle. Une fois que vous avez réduit la cause de l'échec à quelque chose de plus spécifique, vous pouvez le corriger ou le classer, selon le cas.

5
MJRousos

J'ai pu résoudre cette erreur en ajoutant explicitement l'attribut http verb à ma méthode de contrôleur asp.net core 2.x. La convention consistant à préfixer le nom de la méthode avec le verbe http ne suffit apparemment pas pour Swashbuckle.

[HttpPost] public async Task<IActionResult> AddNewData([FromBody] MyType myType) { … }

2
Werewolf

Dans mon cas, je peux reproduire votre erreur en omettant "." à partir du point final comme vous l'avez fait .  enter image description here

Je ne reçois pas l'erreur si j'inclus "." au début du chemin . Voici plus de mon code au cas où il serait pertinent.

Dans ConfigureServices j'ai

 services.AddSwaggerGen(c =>
    {
        c.OperationFilter<AuthorizationHeaderParameterOperationFilter>();

        c.SwaggerDoc("v1", new Info
        {
            Version = "v1",
            Title = "My API",
            Description = "ASP.NET Core Web API",
            TermsOfService = "None",
            Contact = new Contact
            {
                Name = "my name",
                Email = "[email protected]"
            }
        });
    });

Dans configure

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    app.UseAuthentication();

    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
        app.UseDatabaseErrorPage();
    }
    else
    {
        app.UseExceptionHandler("/Error");
        app.UseHsts();
    }

    app.UseHttpsRedirection();
    app.UseStaticFiles();
    app.UseCookiePolicy();


    app.UseRewriter(new RewriteOptions()
        .AddRedirectToHttpsPermanent());

    app.UseSwagger(c =>
    {
        c.RouteTemplate =
            "api-docs/{documentName}/swagger.json";
    });
    app.UseSwaggerUI(c =>
    {
        //Include virtual directory if site is configured so
        c.RoutePrefix = "api-docs";
        c.SwaggerEndpoint("./v1/swagger.json", "Api v1");
    });

    app.UseMvc(routes =>
    {
        routes.MapRoute(
            "default",
            "{controller=Home}/{action=Index}/{id?}");
    });

Il y a aussi

public class AuthorizationHeaderParameterOperationFilter : IOperationFilter
    {
        public void Apply(Operation operation, OperationFilterContext context)
        {
            var filterPipeline = context.ApiDescription.ActionDescriptor.FilterDescriptors;
            var isAuthorized = filterPipeline.Select(filterInfo => filterInfo.Filter).Any(filter => filter is AuthorizeFilter);
            var allowAnonymous = filterPipeline.Select(filterInfo => filterInfo.Filter).Any(filter => filter is IAllowAnonymousFilter);

            if (isAuthorized && !allowAnonymous)
            {
                if (operation.Parameters == null)
                    operation.Parameters = new List<IParameter>();

                operation.Parameters.Add(new NonBodyParameter
                {
                    Name = "Authorization",
                    In = "header",
                    Description = "access token",
                    Required = true,
                    Type = "string"
                });
            }
        }

Mes dépendances sont 

Microsoft.AspNetCore.App (2.1.0)
Swashbuckle.AspNetCore (2.5.0)
Microsoft.NETCore.App (2.1.0)
2
Kirsten Greed