You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swagger UI不显示多API版本但JSON端点正常(ASP.NET Core 8)

ASP.NET Core 8 API版本控制:Swagger UI仅显示v1版本,v2 JSON端点正常

问题现象

已基于Asp.Versioning.Mvc和Asp.Versioning.Mvc.ApiExplorer(v8.1.0)配置API版本控制,v1和v2的JSON端点直接访问均可正常响应,但Swagger UI仅展示v1的API接口,版本选择下拉框未出现v2选项。

Swagger仅显示V1
V1的JSON端点正常
V2的JSON端点正常

相关代码

控制器1(版本1)

[ApiVersion("1.0")]
[Route("api/v{version:apiVersion}/[controller]")]
[ApiController]
public class PostController : Controller
{
    [HttpGet]
    [MapToApiVersion("1.0")]
    [Route("{id}")]
    public IActionResult GetById(int id)
    {
        var post = new Post { Id = id, Text = "Hello, world" };
        return Ok(post);
    }
}

控制器2(版本2)

[ApiVersion("2.0")]
[Route("api/v{version:apiVersion}/[controller]")]
[ApiController]
public class PostController : Controller
{        
    [HttpGet]
    [MapToApiVersion("2.0")]
    [Route("{id}")]
    public IActionResult GetById(int id)
    {
        var post = new Post { Id = id, Text = "Hello, universe" };
        return Ok(post);
    }
}

Program.cs

public class Program
{
    public static void Main(string[] args)
    {
        var builder = WebApplication.CreateBuilder(args);

        builder.Services.AddControllers();

        builder.Services.AddApiVersioning(config =>
        {
            config.DefaultApiVersion = new ApiVersion(1, 0);
            config.AssumeDefaultVersionWhenUnspecified = true;
            config.ReportApiVersions = true; // 响应头添加支持的API版本信息
            config.ApiVersionReader = new UrlSegmentApiVersionReader(); // 从URL路径读取版本号

        })             
            .AddApiExplorer(config =>
            {
                config.GroupNameFormat = "'v'VVV"; // 版本格式
                config.SubstituteApiVersionInUrl = true;
            });

        builder.Services.AddSwaggerGen();

        builder.Services.ConfigureOptions<ConfigureSwaggerOptions>();
        builder.Services.AddEndpointsApiExplorer();

        var app = builder.Build();

        app.UseHttpsRedirection();

        app.UseAuthorization();

        if(app.Environment.IsDevelopment())
        {
            app.UseSwagger();
            app.UseSwaggerUI(options =>
            {
                var provider = app.Services.GetRequiredService<IApiVersionDescriptionProvider>();
                foreach (var description in provider.ApiVersionDescriptions)
                {
                    options.SwaggerEndpoint($"/swagger/{description.GroupName}/swagger.json",
                        description.GroupName.ToUpperInvariant());
                }
            });
        }

        app.MapControllers();
        app.Run();
    }
}

ConfigureSwaggerOptions

public class ConfigureSwaggerOptions : IConfigureNamedOptions<SwaggerGenOptions>
{
    private readonly IApiVersionDescriptionProvider _provider;
    public ConfigureSwaggerOptions(IApiVersionDescriptionProvider provider)
    {
        _provider = provider;
    }

    public void Configure(SwaggerGenOptions options)
    {
        foreach (var description in _provider.ApiVersionDescriptions)
        {
            options.SwaggerDoc(description.GroupName, CreateVersionInfo(description));
        }
    }

    public void Configure(string? name, SwaggerGenOptions options)
    {
        Configure(options);
    }

    private OpenApiInfo CreateVersionInfo(ApiVersionDescription description)
    {
        var info = new OpenApiInfo
        {
            Title = "CwkSocial",
            Version = description.ApiVersion.ToString(),
        };

        if (description.IsDeprecated)
        {
            info.Description = "This API version has been deprecated.";
        }

        return info;
    }
}

解决方案

1. 修复同名控制器冲突

C#不允许同一命名空间下存在两个同名类,此处两个PostController导致框架仅加载v1版本的控制器,API版本探测组件无法识别到v2的存在。

解决方式二选一:

  • 按命名空间拆分:将两个控制器放在不同的子命名空间下,保持类名不变,路由自动匹配正确路径:
    // v1控制器命名空间
    namespace YourApp.Controllers.V1;
    
    [ApiVersion("1.0")]
    [Route("api/v{version:apiVersion}/[controller]")]
    [ApiController]
    public class PostController : Controller { /* ... */ }
    
    // v2控制器命名空间
    namespace YourApp.Controllers.V2;
    
    [ApiVersion("2.0")]
    [Route("api/v{version:apiVersion}/[controller]")]
    [ApiController]
    public class PostController : Controller { /* ... */ }
    
  • 显式指定路由:重命名v2控制器,同时手动指定路由中的控制器名称,避免路由变化:
    [ApiVersion("2.0")]
    [Route("api/v{version:apiVersion}/post")] // 显式写死路由中的post,替代[controller]
    [ApiController]
    public class PostV2Controller : Controller { /* ... */ }
    

2. 调整服务注册顺序

将AddEndpointsApiExplorer移到AddApiVersioning之后、AddSwaggerGen之前,确保API版本探测组件先完成初始化,避免配置覆盖:

builder.Services.AddControllers();

// 先配置API版本控制及API探测
builder.Services.AddApiVersioning(config =>
{
    config.DefaultApiVersion = new ApiVersion(1, 0);
    config.AssumeDefaultVersionWhenUnspecified = true;
    config.ReportApiVersions = true;
    config.ApiVersionReader = new UrlSegmentApiVersionReader();
})             
.AddApiExplorer(config =>
{
    config.GroupNameFormat = "'v'VVV";
    config.SubstituteApiVersionInUrl = true;
});

// 移到此处,确保API版本探测完成后再初始化端点探测
builder.Services.AddEndpointsApiExplorer();

builder.Services.AddSwaggerGen();
builder.Services.ConfigureOptions<ConfigureSwaggerOptions>();

3. 验证效果

重启应用后,Swagger UI的版本下拉框会同时显示v1和v2选项,两个版本的API文档均可正常访问。


内容的提问来源于stack exchange,提问作者Cristian Quesada

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 05:40:53