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";
});
}
}
}
Fast: ASP.NET Core no longer depends on System. Web. dll for browser-server communication. ASP.NET Core allows us to include packages that we need for our application.
Swagger (OpenAPI) is a language-agnostic specification for describing REST APIs. It allows both computers and humans to understand the capabilities of a REST API without direct access to the source code. Its main goals are to: Minimize the amount of work needed to connect decoupled services.
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"}
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");
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With