ASP.NET Core多版本API中NSwag文档授权按钮不显示问题及解决
问题描述
我维护一个配置了多API版本控制的ASP.NET Core 6项目,在使用NSwag生成API文档时,页面右侧的Authorize按钮完全不显示,检查认证配置和Swagger相关代码后仍未找到原因。
原项目代码片段
1. Swagger配置扩展方法
public static IServiceCollection AddSwaggerTest(this IServiceCollection serviceCollection, string apiName) { serviceCollection.AddApiVersioning(opt => { opt.DefaultApiVersion = new Microsoft.AspNetCore.Mvc.ApiVersion(1, 0); opt.AssumeDefaultVersionWhenUnspecified = true; opt.ReportApiVersions = true; opt.ApiVersionReader = ApiVersionReader.Combine(new UrlSegmentApiVersionReader(), new HeaderApiVersionReader("x-api-version"), new MediaTypeApiVersionReader("x-api-version")); }) .AddVersionedApiExplorer(setup => { setup.GroupNameFormat = "'v'VVV"; setup.SubstituteApiVersionInUrl = true; }) .AddSwaggerGen(option => { option.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme { In = ParameterLocation.Header, Description = "Please enter a valid token", Name = "Authorization", Type = SecuritySchemeType.Http, BearerFormat = "JWT", Scheme = "Bearer" }); option.AddSecurityRequirement(new OpenApiSecurityRequirement { { new OpenApiSecurityScheme { Reference = new OpenApiReference { Type=ReferenceType.SecurityScheme, Id="Bearer" } }, new string[]{} } }); option.EnableAnnotations(); option.CustomSchemaIds(x => x.FullName); var provider = serviceCollection.BuildServiceProvider().GetRequiredService<IApiVersionDescriptionProvider>(); foreach (var description in provider.ApiVersionDescriptions) { var xmlPath = Path.Combine(AppContext.BaseDirectory, $"{apiName}.xml"); if (File.Exists(xmlPath)) { option.IncludeXmlComments(xmlPath); } } }) .AddSwaggerDocuments(apiName); return serviceCollection; } private static IServiceCollection AddSwaggerDocuments(this IServiceCollection serviceCollection, string swaggerApiName) { var provider = serviceCollection.BuildServiceProvider().GetRequiredService<IApiVersionDescriptionProvider>(); foreach (var description in provider.ApiVersionDescriptions) { serviceCollection.AddSwaggerDocument(config => { config.DocumentName = description.GroupName.ToLower(); config.PostProcess = document => { document.Info.Version = description.GroupName.ToLower(); document.Info.Title = swaggerApiName; document.Info.Description = description.IsDeprecated ? "This Api version has been depreciated" : "A versioned TTC Fas Rest API"; }; config.ApiGroupNames = new[] { description.GroupName.ToLower() }; }); } return serviceCollection; }
2. 原Program.cs配置
// 其他代码... builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerTest("test"); builder.Services.AddAuthentication(authOptions => { authOptions.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; authOptions.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; }) .AddJwtBearer(jwtOptions => { jwtOptions.SaveToken = true; jwtOptions.TokenValidationParameters = new TokenValidationParameters { ValidateAudience = true, ValidateIssuer = true, ValidateLifetime = true, ValidateIssuerSigningKey = true, ValidIssuer = "Test", ValidAudience = "https://localhost:7117", IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("superSecretKey@2410")) }; }); var app = builder.Build(); // 配置HTTP请求管道 if (app.Environment.IsDevelopment()) { app.UseOpenApi(); app.UseSwaggerUi3(config => config.DocExpansion = "list"); } app.UseHttpsRedirection(); app.UseAuthentication(); app.UseAuthorization(); app.MapControllers(); app.Run();
3. V1控制器
[ApiController] [ApiVersion("1.0")] [Route("api/v{version:apiVersion}/[controller]")] public class WeatherForecastController : ControllerBase { private static readonly string[] Summaries = new[] { "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching", "v1" }; private readonly ILogger<WeatherForecastController> _logger; public WeatherForecastController(ILogger<WeatherForecastController> logger) { _logger = logger; } [Authorize] [HttpGet(Name = "GetWeatherForecast")] public IEnumerable<WeatherForecast> Get() { return Enumerable.Range(1, 5).Select(index => new WeatherForecast { Date = DateTime.Now.AddDays(index), TemperatureC = Random.Shared.Next(-20, 55), Summary = Summaries[Random.Shared.Next(Summaries.Length)] }) .ToArray(); } }
4. V2控制器
[ApiController] [ApiVersion("2.0")] [Route("api/v{version:apiVersion}/[controller]")] public class WeatherForecastController : ControllerBase { private static readonly string[] Summaries = new[] { "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching", "v2" }; private readonly ILogger<WeatherForecastController> _logger; public WeatherForecastController(ILogger<WeatherForecastController> logger) { _logger = logger; } [Authorize] [HttpGet(Name = "GetWeatherForecast")] public IEnumerable<WeatherForecast> Get() { return Enumerable.Range(1, 5).Select(index => new WeatherForecast { Date = DateTime.Now.AddDays(index), TemperatureC = Random.Shared.Next(-20, 55), Summary = Summaries[Random.Shared.Next(Summaries.Length)] }) .ToArray(); } }
注:项目中V1和V2版本都配置了登录控制器。
解决方案
最终解决方法是替换NSwag组件为Swashbuckle.AspNetCore:
- 移除
NSwag.AspNetCoreNuGet包 - 安装
Swashbuckle.AspNetCore和Swashbuckle.AspNetCore.SwaggerUINuGet包 - 保持
AddSwaggerTest扩展方法及控制器代码不变,修改Program.cs中的Swagger相关配置:
// 其他代码... builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerTest("test"); builder.Services.AddAuthentication(authOptions => { authOptions.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; authOptions.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; }) .AddJwtBearer(jwtOptions => { jwtOptions.SaveToken = true; jwtOptions.TokenValidationParameters = new TokenValidationParameters { ValidateAudience = true, ValidateIssuer = true, ValidateLifetime = true, ValidateIssuerSigningKey = true, ValidIssuer = "Test", ValidAudience = "https://localhost:7117", IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("superSecretKey@2410")) }; }); var app = builder.Build(); var apiVersionDescriptionProvider = app.Services.GetRequiredService<IApiVersionDescriptionProvider>(); // 配置HTTP请求管道 if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(c => { foreach (var description in apiVersionDescriptionProvider.ApiVersionDescriptions) { c.SwaggerEndpoint($"/swagger/{description.GroupName}/swagger.json", description.GroupName.ToUpperInvariant()); } }); } app.UseHttpsRedirection(); app.UseAuthentication(); app.UseAuthorization(); app.MapControllers(); app.Run();
完成上述修改后,Swagger文档页面的Authorize按钮正常显示,且多API版本的认证功能正常工作。
内容的提问来源于stack exchange,提问作者Hoang Minh
相关产品推荐
相关产品推荐

