Переход на .NET Core 2.1 нарушает работу пользовательского интерфейса Swagger

Недавно мы перенесли наш проект с .NET Core 2.0 на .NET Core 2.1. В результате наш сайт документации Swagger перестал работать. Мы все еще можем получить к нему доступ. Мы можем видеть измененный заголовок и версию, но документации по API нет, только сообщение No operations defined in spec!.

Я пробовал более старое решение для .NET Core 2.0, но это не помогло. На основании следующих двух статей 1 2 Я пробовал удаление атрибутов Swagger из методов контроллера и добавление атрибута [ApiController] над классом контроллера, но это тоже не помогло. Может ли кто-нибудь помочь решить эту проблему?

.csproj

<Project Sdk="Microsoft.NET.Sdk.Web">

    <PropertyGroup>
        <TargetFramework>netcoreapp2.1</TargetFramework>
        <RootNamespace>Company.Administration.Api</RootNamespace>
        <AssemblyName>Company.Administration.Api</AssemblyName>
        <PackageId>Company.Administration.Api</PackageId>
        <Authors></Authors>
        <Company>Company, Inc</Company>
        <Product>Foo</Product>
        <ApplicationInsightsResourceId>/subscriptions/dfa7ef88-f5b4-45a8-9b6c-2fb145290eb4/resourcegroups/Foo/providers/microsoft.insights/components/foo</ApplicationInsightsResourceId>
        <ApplicationInsightsAnnotationResourceId>/subscriptions/dfa7ef88-f5b4-45a8-9b6c-2fb145290eb4/resourceGroups/Foo/providers/microsoft.insights/components/foo</ApplicationInsightsAnnotationResourceId>
        <UserSecretsId>bf821b77-3f23-47e8-834e-7f72e2ab00c5</UserSecretsId>
    </PropertyGroup>

    <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
        <DocumentationFile>bin\Debug\netcoreapp2.1\Administration.Api.xml</DocumentationFile>
    </PropertyGroup>

    <PropertyGroup>
        <!-- Try to set version using environment variables set by GitVersion. -->
        <Version Condition=" '$(Version)' == '' And '$(GitVersion_AssemblySemVer)' != '' ">$(GitVersion_AssemblySemVer)</Version>
        <InformationalVersion Condition=" '$(InformationalVersion)' == '' And '$(GitVersion_InformationalVersion)' != '' ">$(GitVersion_InformationalVersion)</InformationalVersion>

        <!-- If we don't have environment variables set by GitVersion, use default version. -->
        <Version Condition=" '$(Version)' == '' ">0.0.1</Version>
        <InformationalVersion Condition=" '$(InformationalVersion)' == '' ">0.0.1-local</InformationalVersion>
    </PropertyGroup>

    <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
        <DocumentationFile>bin\Release\netcoreapp2.1\Administration.Api.xml</DocumentationFile>
    </PropertyGroup>

    <PropertyGroup>
        <MvcRazorCompileOnPublish>false</MvcRazorCompileOnPublish>
        <PreserveCompilationContext>false</PreserveCompilationContext>
    </PropertyGroup>

    <ItemGroup>
        <Folder Include="wwwroot\" />
    </ItemGroup>

    <ItemGroup>
        <PackageReference Include="IdentityModel" Version="3.7.0-preview1" />
        <PackageReference Include="IdentityServer4.AccessTokenValidation" Version="2.6.0" />
        <PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.3.0" />
        <PackageReference Include="Microsoft.AspNet.WebApi.Client" Version="5.2.6" />
        <PackageReference Include="Microsoft.AspNetCore.All" Version="2.1.0" />
        <PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="2.1.0" />
        <PackageReference Include="Swashbuckle.AspNetCore" Version="2.4.0" />
        <PackageReference Include="Swashbuckle.AspNetCore.ReDoc" Version="2.4.0" />
    </ItemGroup>

    <ItemGroup>
        <WCFMetadata Include="Connected Services" />
    </ItemGroup>

</Project>

Startup.cs

using Company.Administration.Api.Controllers;
using Company.Administration.Api.Security;
using Company.Administration.Api.Services;
using Company.Administration.Api.Swagger;
using Company.Administration.Api.Swagger.Examples;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.PlatformAbstractions;
using Newtonsoft.Json.Converters;
using Swashbuckle.AspNetCore.Swagger;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.IO;
using System.Net.Http;

namespace Company.Administration.Api
{
    public class Startup
    {
        public Startup(IConfiguration configuration, ILogger<Startup> logger, IHostingEnvironment hostingEnvironment)
        {
            Configuration = configuration;
            Logger = logger;
            HostingEnvironment = hostingEnvironment;

            JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
        }

        public IHostingEnvironment HostingEnvironment { get; }
        public IConfiguration Configuration { get; }
        public ILogger Logger { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddTransient<HttpClient>();
            services.AddTransient<AuthService>();
            services.AddTransient<FooAdministrationService>();

            services.AddMvc()
                .AddJsonOptions(options =>
                {
                    options.SerializerSettings.Converters.Add(new StringEnumConverter());
                });

            services.AddFooAuthentication(Configuration);

            services.AddFooAuthorization();

            services.AddCors();

            services
                .AddSwaggerGen(c =>
                {
                    c.SwaggerDoc("v1", new Info { Title = "Administration", Version = "v1" });

                    var basePath = PlatformServices.Default.Application.ApplicationBasePath;
                    var xmlPath = Path.Combine(basePath, "Administration.Api.xml");
                    if (File.Exists(xmlPath))
                    {
                        c.IncludeXmlComments(xmlPath);
                    }
                    else
                    {
                        Logger.LogWarning($@"File does not exist: ""{xmlPath}""");
                    }

                    string authorityOption = Configuration["IdentityServerAuthentication:Url"] ?? throw new Exception("Failed to load authentication URL from configuration.");
                    string authority = $"{authorityOption}{(authorityOption.EndsWith("/") ? "" : "/")}";

                    var scopes = new Dictionary<string, string>
                    {
                        { "api", "Allow calls to the Foo administration API." }
                    };

                    c.AddSecurityDefinition("OpenId Connect", new OAuth2Scheme
                    {
                        Type = "oauth2",
                        Flow = "implicit",
                        AuthorizationUrl = $"{authority}connect/authorize",
                        TokenUrl = $"{authority}connect/token",
                        Scopes = scopes
                    });

                    c.DescribeAllEnumsAsStrings();

                    c.OperationFilter<ExamplesOperationFilter>(services.BuildServiceProvider());
                })
                .ConfigureSwaggerGen(options =>
                {
                    options.CustomSchemaIds(t => t.FullName);

                    options.OperationFilter<SecurityRequirementsOperationFilter>();
                });
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseCors(builder => builder
                .AllowAnyOrigin()
                .AllowAnyMethod()
                .AllowAnyHeader()
                .AllowCredentials()
                .WithExposedHeaders(AdministrationControllerBase.ExposedHeaders));

            app.UseAuthentication();

            app.UseMvc()
                .UseSwagger(x => x.RouteTemplate = "api-docs/{documentName}/swagger.json")
                .UseSwaggerUI(c =>
                {
                    c.OAuthClientId("foo-administration.swagger");
                    c.RoutePrefix = "api-docs";
                    c.SwaggerEndpoint("v1/swagger.json", "Foo Administration API");
                });

            app.UseReDoc(options =>
            {
                options.RoutePrefix = "api-docs-redoc";
                options.SpecUrl = "../api-docs/v1/swagger.json";
            });

        }
    }
}

person lss    schedule 31.05.2018    source источник
comment
У меня есть служба, работающая на 2.1 RC1 с Swashbuckle.AspNetCore 2.4.0, и она отлично работает. Может быть, если вы разместите файл своего проекта и startup.cs, мы увидим, что не так.   -  person Hans Kilian    schedule 31.05.2018
comment
Я обновил свой сервис до версии 2.1, и он по-прежнему работает нормально.   -  person Hans Kilian    schedule 31.05.2018
comment
Я добавил требуемые файлы.   -  person lss    schedule 31.05.2018


Ответы (6)


Я знаю, что на это уже был дан ответ, но просто подумал, что перезвоню всем, кто сталкивается с этим и все еще ищет ответ. Если вы попытаетесь перейти непосредственно к файлу json, он предоставит вам причину, по которой он не работает.

Пример:

В адресной строке: https://localhost:44300/swagger/v1/swagger.json

Сообщение об ошибке возвращено:

{"Путь": "/ swagger / v1 / swagger.json", "Запущено": false, "Состояние": "Внутренняя ошибка сервера", "Msg": "Неоднозначный HTTP-метод действия - Controllers.ChatMessageController.ListFriends (Project .API). Действия требуют явной привязки HttpMethod для Swagger 2.0 "}

person Briana Finney    schedule 09.10.2019
comment
Выявлено. Конфликт маршрута. Решил проблему. Спасибо - person Observer; 13.02.2020
comment
Это помогает разобраться в общем. Очень признателен! - person Anders Juul; 05.04.2020
comment
Для меня это помогло просто открыть окно вывода в VS и пройти через каждую ошибку из swagger и исправить их до тех пор, пока пользовательский интерфейс, наконец, не отобразится. Я не получил ответа в самом браузере. Кроме того, этот ответ очень помог - person kumaheiyama; 25.11.2020

С ядром C # .Net, если вы используете несколько методов с декоратором [HttpPost] или [HttpGet], файл /swagger/v1/swagger.json не может быть сгенерирован, очевидно, из-за неоднозначности всех методов POST или GET.

Если локально работает нормально, но возникают проблемы после хостинга IIS, значит, у вас проблема с swagger.json относительным путем.

Тогда попробуйте это ниже внутри ConfigureServices:

app.UseSwaggerUI(c =>
{
string swaggerJsonBasePath = string.IsNullOrWhiteSpace(c.RoutePrefix) ? "." : "..";
c.SwaggerEndpoint($"{swaggerJsonBasePath}/swagger/v1/swagger.json", "My API");
});
person Rousonur Jaman    schedule 23.05.2019

Я попытался воссоздать одно и то же решение построчно. Swagger работал, пока я не добавил <PreserveCompilationContext>false</PreserveCompilationContext> в .csproj файл. Удаление этого атрибута привело к повторному появлению пользовательского интерфейса Swagger.

person lss    schedule 01.06.2018

Я обновил свой проект с 2.2 до 3.1 и столкнулся с той же проблемой даже после попытки выполнить все указанные шаги. По некоторым причинам у меня не было [ApiController] над моим классом контроллера. Когда я его добавил, появился чванливый документ ...

person user007    schedule 21.03.2020

Для меня эта ошибка возникла из-за того, что я попытался использовать атрибут [ApiVersionNeutral] на контроллере v1 и [ApiVersion (2.0)] на контроллере v2. В любом случае вы можете получить полное сообщение об ошибке в средствах вывода или диагностики (вкладка «События»). И вот я получил это сообщение:

System.NotSupportedException: HTTP-метод GET и api пути / Сотрудники, перегруженные действиями - Api.V2.Controllers.EmployeesController.Get, Api.V1.Controllers.EmployeesController.Get.

Для действий требуется уникальная комбинация метода и пути для Swagger 2.0. Используйте ConflictingActionsResolver как временное решение

person Gilberto Alexandre    schedule 25.06.2018
comment
Я тоже получаю эту ошибку. Вы смогли исправить? - person Ryan Buening; 19.07.2018
comment
Эта ошибка возникла в двух ситуациях ... Я помню только ранее упомянутые. Итак, я решил эту проблему, поставив [ApiVersion (1.0)] на контроллер. Контроллеры с атрибутом ApiVersion не могут использовать атрибут нейтральной версии в другом файле. - person Gilberto Alexandre; 19.07.2018

Эта ошибка также может быть вызвана другими проблемами в вашем API. В моем случае у swagger была проблема с пониманием перегрузки с двумя действиями Get в одном из моих контроллеров. Сначала сработало исследование исключения, которое произошло при загрузке спецификации Open API следующим образом:

{https: // {хост}: {порт} / swagger / {SwaggerSpecName} /swagger.json}

затем появилась ошибка, мешающая загрузке спецификации OpenAPI. Позже я разрешил ошибку (перегрузка действий get, как я упоминал выше), применив эту спецификацию к методу swager gen.

 services.AddSwaggerGen(c => 
  { 
    other configs...;
    c.ResolveConflictingActions(apiDescriptions => apiDescriptions.First());
  });

Вот сообщение, которое привело меня к ответу

person Damien Doumer    schedule 22.09.2019