如何在ASP.NET Core Minimal API生成的OpenAPI规范中配置Server URL路径
我有一个基于C#/ASP.NET Core的Minimal API应用,通过Swagger生成OpenAPI规范。为匹配发布yml文件要求,需要把路径前缀(/api/v2)加到Swagger的Server配置中,同时移除路由组里的该前缀。
最初的Swagger配置代码如下:
.AddSwaggerGen(c => { c.SwaggerDoc("v1", new OpenApiInfo { Title = "My API", Version = "2.0", }); c.AddServer(new OpenApiServer { Url = "https://api.myapi.org.uk/api/v2", Description = "Production" }); c.AddServer(new OpenApiServer { Url = "https://localhost:{port}/api/v2", Description = "Local", Variables = { new ("port", new OpenApiServerVariable { Default = "7147", }), }, }); })
对应的路由组配置:
var builder = app.MapGroup("/needs"); builder.MapGet("/", async (CancellationToken cancellationToken) => { ... });
但用Swagger UI测试时,所有请求都返回404。如果把/api/v2移到路由组里,同时简化Server的URL配置,就能正常工作:
.AddSwaggerGen(c => { c.SwaggerDoc("v1", new OpenApiInfo { Title = "My API", Version = "2.0", }); c.AddServer(new OpenApiServer { Url = "https://api.myapi.org.uk/", Description = "Production" }); c.AddServer(new OpenApiServer { Url = "https://localhost:{port}/", Description = "Local", Variables = { new ("port", new OpenApiServerVariable { Default = "7147", }), }, }); }) // ... var builder = app.MapGroup("/api/v2/needs"); builder.MapGet("/", async (CancellationToken cancellationToken) => { ... });
我试过多种组合都没解决问题,想知道这是OpenAPI规范的实现问题,还是有可行的解决办法?最终需要生成的OpenAPI结构如下:
openapi: 3.0.1 info: title: My API version: '2.0' servers: - url: https://myapi.org.uk/api/v2 description: Production - url: 'https://localhost:{port}' description: Local variables: port: default: '7147' paths: /needs: get: ...
解决方案
问题根源
Swagger UI会直接把Server配置的URL和OpenAPI文档里paths中的路径拼接发起请求。初始配置中,Server URL是https://api.myapi.org.uk/api/v2,paths里的路径是/needs,所以Swagger UI会请求https://api.myapi.org.uk/api/v2/needs,但实际API并没有/api/v2前缀,因此返回404。
要同时满足OpenAPI文档结构要求和API正常响应,有两种可行方法:
方法1:配置API的PathBase前缀(推荐)
在Program.cs的路由配置前添加UsePathBase,给整个API加上/api/v2前缀:
// 在app.MapGroup之前添加 app.UsePathBase("/api/v2"); // 路由组保持不变 var builder = app.MapGroup("/needs"); builder.MapGet("/", async (CancellationToken cancellationToken) => { ... });
然后调整Swagger的Server配置,去掉URL里的/api/v2:
c.AddServer(new OpenApiServer { Url = "https://api.myapi.org.uk/", Description = "Production" }); c.AddServer(new OpenApiServer { Url = "https://localhost:{port}/", Description = "Local", Variables = { new ("port", new OpenApiServerVariable { Default = "7147", }), }, });
如果需要paths显示为/needs而非/api/v2/needs,可以配合下面的文档过滤器调整。
方法2:用文档过滤器修改OpenAPI路径
如果不能修改API实际路由前缀,或必须让paths显示为/needs,可以自定义Swagger文档过滤器,移除生成路径中的/api/v2前缀:
首先添加过滤器类:
public class RemovePathPrefixFilter : IDocumentFilter { public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context) { var updatedPaths = new OpenApiPaths(); foreach (var pathEntry in swaggerDoc.Paths) { // 移除路径中的/api/v2前缀 var cleanedPath = pathEntry.Key.Replace("/api/v2", string.Empty); updatedPaths.Add(cleanedPath, pathEntry.Value); } swaggerDoc.Paths = updatedPaths; } }
然后在Swagger配置中注册这个过滤器:
.AddSwaggerGen(c => { // 其他配置... c.DocumentFilter<RemovePathPrefixFilter>(); // Server配置保持目标结构 c.AddServer(new OpenApiServer { Url = "https://api.myapi.org.uk/api/v2", Description = "Production" }); c.AddServer(new OpenApiServer { Url = "https://localhost:{port}", Description = "Local", Variables = { new ("port", new OpenApiServerVariable { Default = "7147", }), }, }); })
同时确保API实际路由前缀为/api/v2(通过UsePathBase或路由组前缀实现),这样Swagger UI拼接的请求路径就能匹配实际API路由。
内容的提问来源于stack exchange,提问作者James

