ASP.NET Core Minimal API如何通过请求头实现接口版本控制?
ASP.NET Core Minimal API 基于请求头的版本控制实现及Swagger支持
一、实现基于请求头的路由区分
你的代码中两个/save路由被视为重复,是因为它们的路径完全一致,ASP.NET Core无法自动区分。要实现基于X-Api-Version请求头的路由匹配,有两种优雅的实现方式:
方法1:自定义路由约束
通过自定义路由约束,让ASP.NET Core根据请求头值匹配对应的处理程序:
- 创建自定义约束类:
public class ApiVersionHeaderConstraint : IRouteConstraint { private readonly string _requiredVersion; public ApiVersionHeaderConstraint(string requiredVersion) { _requiredVersion = requiredVersion; } public bool Match(HttpContext httpContext, IRouter route, string routeKey, RouteValueDictionary values, RouteDirection routeDirection) { return httpContext.Request.Headers.TryGetValue("X-Api-Version", out var version) && version.Equals(_requiredVersion, StringComparison.OrdinalIgnoreCase); } }
- 注册约束到路由配置:
builder.Services.Configure<RouteOptions>(options => { options.ConstraintMap.Add("apiVersion", typeof(ApiVersionHeaderConstraint)); });
- 定义带约束的路由:
app.MapPost("/save", ([FromBody] SaveRequestV1 request) => "Do something for v1") .WithMetadata(new RouteConstraintMetadata("apiVersion", "v1")); app.MapPost("/save", ([FromBody] SaveRequestV2 request) => "Do another thing for v2") .WithMetadata(new RouteConstraintMetadata("apiVersion", "v2"));
方法2:使用MapWhen筛选请求
如果不想自定义约束,可以用MapWhen按请求头分组处理:
// 处理v1版本请求 app.MapWhen(context => context.Request.Headers.TryGetValue("X-Api-Version", out var version) && version.Equals("v1", StringComparison.OrdinalIgnoreCase), v1App => { v1App.MapPost("/save", ([FromBody] SaveRequestV1 request) => "Do something for v1"); }); // 处理v2版本请求 app.MapWhen(context => context.Request.Headers.TryGetValue("X-Api-Version", out var version) && version.Equals("v2", StringComparison.OrdinalIgnoreCase), v2App => { v2App.MapPost("/save", ([FromBody] SaveRequestV2 request) => "Do another thing for v2"); });
二、Swagger支持基于请求头的版本控制
Swagger完全支持这种版本控制方式,只需配置多版本文档并添加请求头参数:
- 配置Swagger生成器:
builder.Services.AddSwaggerGen(options => { // 注册v1和v2版本的文档 options.SwaggerDoc("v1", new OpenApiInfo { Title = "My API", Version = "v1" }); options.SwaggerDoc("v2", new OpenApiInfo { Title = "My API", Version = "v2" }); // 添加全局请求头参数过滤器 options.OperationFilter<ApiVersionHeaderFilter>(); }); // 自定义操作过滤器,自动添加X-Api-Version请求头 public class ApiVersionHeaderFilter : IOperationFilter { public void Apply(OpenApiOperation operation, OperationFilterContext context) { var apiVersion = context.ApiDescription.GroupName; operation.Parameters.Add(new OpenApiParameter { Name = "X-Api-Version", In = ParameterLocation.Header, Required = true, Schema = new OpenApiSchema { Type = "string", Default = new OpenApiString(apiVersion) } }); } }
- 配置Swagger UI中间件:
app.UseSwagger(); app.UseSwaggerUI(options => { options.SwaggerEndpoint("/swagger/v1/swagger.json", "My API v1"); options.SwaggerEndpoint("/swagger/v2/swagger.json", "My API v2"); });
这样在Swagger UI中选择不同版本的文档时,会自动填充对应的X-Api-Version请求头,也可以手动修改值测试不同版本接口。
补充说明
- 你尝试的
[FromHeader(Name = "X-Api-Version:regex(^1$)")]写法不被支持,FromHeader的Name属性仅用于指定请求头名称,不支持正则约束逻辑。 - 若使用
SaveVersion枚举,也可以在同一个路由处理程序中分支判断版本,但这种方式会将不同版本的业务逻辑耦合在一起,不如上述两种方式清晰。
内容的提问来源于stack exchange,提问作者Endy Tjahjono
相关产品推荐
相关产品推荐

