迁移到 .NET Core 2.1 破坏了 Swagger UI
Migrating to .NET Core 2.1 breaks Swagger UI
最近我们将项目从 .NET Core 2.0
迁移到了 .NET Core 2.1
。结果我们的 Swagger 文档站点停止工作。我们仍然可以访问它。我们可以看到自定义的标题和版本,但是没有 API 文档,只有一条消息说 No operations defined in spec!
.
我尝试了一个较旧的解决方案 , but it did not help. Based on the following two articles 1 2我尝试从控制器方法中删除 Swagger 属性并在控制器上方添加一个 [ApiController]
属性 class,但这也无济于事.谁能帮忙解决这个问题?
.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";
});
}
}
}
我试图逐行重新创建相同的解决方案。在我将 <PreserveCompilationContext>false</PreserveCompilationContext>
添加到 .csproj
文件之前,Swagger 一直有效。删除此属性导致 Swagger UI 重新出现。
对我来说,这个错误出现了,为什么我尝试在控制器 v1 上使用 [ApiVersionNeutral] 属性,并在控制器 v2 上使用 [ApiVersion("2.0")] 。无论如何,您可以在输出或诊断工具(事件选项卡)中获得完整的错误消息。所以我收到了这条消息:
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
对于 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");
});
此错误也可能是由您 API 中的其他问题引起的。
就我而言,swagger 在理解我的一个控制器中的两个 Get 操作的重载时遇到了问题。
起作用的是首先调查通过加载 Open api 规范发生的异常,如下所示:
{https://{host}:{port}/swagger/{SwaggerSpecName}/swagger.json}
然后出现阻碍 OpenAPI 规范加载的错误。
后来,我通过将此规范应用于swager gen方法解决了错误(Overload of get actions as I above)。
services.AddSwaggerGen(c =>
{
other configs...;
c.ResolveConflictingActions(apiDescriptions => apiDescriptions.First());
});
Here 是 post 引导我找到答案的
我知道这个问题已经得到解答,但只是想为遇到此问题并仍在寻找答案的任何人插话。如果您尝试直接转到 json 文件,它会为您提供它不起作用的原因。
示例:
在地址栏中:
https://localhost:44300/swagger/v1/swagger.json
返回错误信息:
{"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"}
我将我的项目从 2.2 升级到 3.1,即使在尝试了提供的所有步骤后仍面临同样的问题。由于某些原因,我的控制器 class 上方没有 [ApiController]
。当我添加它时,swagger 文档出现了...
最近我们将项目从 .NET Core 2.0
迁移到了 .NET Core 2.1
。结果我们的 Swagger 文档站点停止工作。我们仍然可以访问它。我们可以看到自定义的标题和版本,但是没有 API 文档,只有一条消息说 No operations defined in spec!
.
我尝试了一个较旧的解决方案 [ApiController]
属性 class,但这也无济于事.谁能帮忙解决这个问题?
.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";
});
}
}
}
我试图逐行重新创建相同的解决方案。在我将 <PreserveCompilationContext>false</PreserveCompilationContext>
添加到 .csproj
文件之前,Swagger 一直有效。删除此属性导致 Swagger UI 重新出现。
对我来说,这个错误出现了,为什么我尝试在控制器 v1 上使用 [ApiVersionNeutral] 属性,并在控制器 v2 上使用 [ApiVersion("2.0")] 。无论如何,您可以在输出或诊断工具(事件选项卡)中获得完整的错误消息。所以我收到了这条消息:
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
对于 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");
});
此错误也可能是由您 API 中的其他问题引起的。 就我而言,swagger 在理解我的一个控制器中的两个 Get 操作的重载时遇到了问题。 起作用的是首先调查通过加载 Open api 规范发生的异常,如下所示:
{https://{host}:{port}/swagger/{SwaggerSpecName}/swagger.json}
然后出现阻碍 OpenAPI 规范加载的错误。 后来,我通过将此规范应用于swager gen方法解决了错误(Overload of get actions as I above)。
services.AddSwaggerGen(c =>
{
other configs...;
c.ResolveConflictingActions(apiDescriptions => apiDescriptions.First());
});
Here 是 post 引导我找到答案的
我知道这个问题已经得到解答,但只是想为遇到此问题并仍在寻找答案的任何人插话。如果您尝试直接转到 json 文件,它会为您提供它不起作用的原因。
示例:
在地址栏中: https://localhost:44300/swagger/v1/swagger.json
返回错误信息:
{"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"}
我将我的项目从 2.2 升级到 3.1,即使在尝试了提供的所有步骤后仍面临同样的问题。由于某些原因,我的控制器 class 上方没有 [ApiController]
。当我添加它时,swagger 文档出现了...