14

Recently we have migrated our project from .NET Core 2.0 to .NET Core 2.1. As a result our Swagger documentation site stopped working. We are still able to access it. We can see the customized title and version, but there is no API documentation, just a message saying No operations defined in spec!.

I have tried an older solution for .NET Core 2.0, but it did not help. Based on the following two articles 1 2 I have tried removing the Swagger attributes from controller methods and adding an [ApiController] attribute above the controller class, but that did not help either. Can anyone help to solve this issue?

.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";
            });

        }
    }
}
lss
  • 1,235
  • 3
  • 15
  • 24

6 Answers6

23

I know this has already been answered, however just thought I'd chime in for anyone who runs into this and still looking for an answer. If you try to go directly to the json file it will provide you a reason why it's not working.

Example:

In the Address Bar: https://localhost:44300/swagger/v1/swagger.json

Error Message Returned:

{"Path":"/swagger/v1/swagger.json","Started":false,"State":"Internal Server Error","Msg":"Ambiguous HTTP method for action - Controllers.ChatMessageController.ListFriends (Project.API). Actions require an explicit HttpMethod binding for Swagger 2.0"}

Briana Finney
  • 1,171
  • 5
  • 12
  • 22
  • 1
    Identified. Route conflict. Solved the problem. Thanks – Observer Feb 13 '20 at 14:19
  • This is help to sort it out in generel. Much appreciated! – Anders Juul Apr 05 '20 at 17:49
  • For me it helped to just have the Output view in VS open and go through every error from swagger and correct them until the UI finally showed. I got no response in the browser itself. Also, [this answer](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/issues/1713#issuecomment-646557710) helped a lot – kumaheiyama Nov 25 '20 at 10:07
13

With C# .Net core if you use several methods with the decorator [HttpPost] or [HttpGet] the /swagger/v1/swagger.json file cannot be generated obviously because of the ambiguity of all the POST or GET methods.

If, locally working fine but getting problem after IIS hosting then you have a problem with swagger.json relative path.

Then try this below inside ConfigureServices:

app.UseSwaggerUI(c =>
{
string swaggerJsonBasePath = string.IsNullOrWhiteSpace(c.RoutePrefix) ? "." : "..";
c.SwaggerEndpoint($"{swaggerJsonBasePath}/swagger/v1/swagger.json", "My API");
});
Rousonur Jaman
  • 1,183
  • 14
  • 19
12

I tried to recreate the same solution line by line. Swagger worked until I added <PreserveCompilationContext>false</PreserveCompilationContext> into the .csproj file. Removing this attribute caused the Swagger UI to reappear.

lss
  • 1,235
  • 3
  • 15
  • 24
1

I upgraded my project from 2.2 to 3.1 and was facing the same problem even after attempting all the steps provided. For some reasons, I didn't have the [ApiController] above my controller class. When I added it, swagger document showed up...

user007
  • 1,504
  • 2
  • 18
  • 51
0

To me, this error appeared why I tried using [ApiVersionNeutral] attribute on controller v1, and [ApiVersion("2.0")] on controller v2. Anyway, you can get full error message in Output or Diagnostics Tools (Events tab). And so I got this message:

System.NotSupportedException: HTTP method "GET" & path "api/Employees" overloaded by actions - Api.V2.Controllers.EmployeesController.Get ,Api.V1.Controllers.EmployeesController.Get.

Actions require unique method/path combination for Swagger 2.0. Use ConflictingActionsResolver as a workaround

Community
  • 1
  • 1
Gilberto Alexandre
  • 2,227
  • 1
  • 18
  • 20
  • I'm getting this error as well. Were you able to fix? – Ryan Buening Jul 19 '18 at 15:42
  • 1
    This error appeared on two situations... I remember only previous mentioned. So, I solved this putting [ApiVersion("1.0")] on controller. Controllers that has ApiVersion attribute cannot cannot use the neutral version attribute on another file. – Gilberto Alexandre Jul 19 '18 at 20:52
0

This error might also be caused by other issues in your API. In my case, swagger had a problem understanding an overload with two Get actions in one of my controllers. What worked was first investigating the exception that occured by loading the Open api spec as follow:

{https://{host}:{port}/swagger/{SwaggerSpecName}/swagger.json}

then the error hindering the OpenAPI spec from loading appeared. Later, I resolved the error (Overload of get actions as I mentioned above) by applying this specification to the swager gen method.

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

Here is the post which lead me to the answer

Damien Doumer
  • 1,991
  • 1
  • 18
  • 33