控制器ApiVersion属性能否含下划线?遗留WebAPI适配Swagger问题
嘿,这个问题我刚好踩过坑!核心原因是ASP.NET Core的API版本验证默认遵循语义化版本规范,只认点号分隔的格式(比如1.0),下划线_属于非法字符,所以[ApiVersion("1_0")]会直接抛出异常。下面给你两个可行的解决方案,按需选择:
方案1:自定义API版本解析规则,兼容下划线格式
这个方案是让系统直接识别下划线分隔的版本号,不需要修改现有路由结构。你需要自定义一个版本解析器,把下划线替换成点号后交给默认解析器处理:
// 在Program.cs(或Startup.cs)中配置API版本 builder.Services.AddApiVersioning(options => { // 替换默认的版本解析器为自定义实现 options.ApiVersionParser = new UnderscoreApiVersionParser(); // 开启响应头返回版本信息,方便调试 options.ReportApiVersions = true; }); // 自定义版本解析器类 public class UnderscoreApiVersionParser : IApiVersionParser { public bool TryParse(string input, out ApiVersion version) { // 将下划线替换为点号,适配默认解析规则 var normalizedVersion = input.Replace('_', '.'); return ApiVersionParser.Default.TryParse(normalizedVersion, out version); } public bool IsMatch(string input) { // 匹配包含下划线的版本号,或者默认格式的版本号 return input.Contains('_') || ApiVersionParser.Default.IsMatch(input); } }
配置完成后,[ApiVersion("1_0")]就能正常工作,路由里的api/{version}/account也能正确匹配1_0、1_1这类格式的版本号。
方案2:保持ApiVersion用标准格式,路由层做兼容
如果不想修改版本解析规则,你可以在控制器上用标准的点号版本(1.0),但路由路径保留下划线格式,同时配置Swagger关联对应版本:
// 控制器上使用标准ApiVersion格式,路由保留下划线 [ApiVersion("1.0")] [Route("api/1_0/account")] public class AccountV10Controller : ControllerBase { // 你的接口方法 [HttpGet] public IActionResult Get() => Ok("V1.0 Account Data"); } [ApiVersion("1.1")] [Route("api/1_1/account")] public class AccountV11Controller : ControllerBase { [HttpGet] public IActionResult Get() => Ok("V1.1 Account Data"); }
然后配置Swagger,让它正确识别控制器对应的版本文档:
builder.Services.AddSwaggerGen(options => { // 注册每个下划线版本的Swagger文档 options.SwaggerDoc("v1_0", new OpenApiInfo { Title = "My Legacy API", Version = "1_0" }); options.SwaggerDoc("v1_1", new OpenApiInfo { Title = "My Legacy API", Version = "1_1" }); // 配置文档包含规则,关联控制器的ApiVersion和Swagger文档版本 options.DocInclusionPredicate((docName, apiDesc) => { if (!apiDesc.TryGetMethodInfo(out var methodInfo)) return false; // 获取控制器上的ApiVersion属性 var controllerVersions = methodInfo.DeclaringType .GetCustomAttributes<ApiVersionAttribute>(true) .SelectMany(attr => attr.Versions); // 把Swagger文档的版本(比如v1_0)转成标准格式,和控制器版本匹配 var targetVersion = docName.Replace("v", "").Replace('_', '.'); return controllerVersions.Any(v => v.ToString() == targetVersion); }); });
额外注意点
- 如果你使用的是旧版的
Microsoft.AspNetCore.Mvc.Versioning包(比如2.x版本),配置方式会略有不同,核心思路还是自定义解析或路由映射。 - 测试时要确保所有遗留路由(比如
/api/1_0/account)都能正常访问,同时Swagger文档能正确展示对应版本的接口。
内容的提问来源于stack exchange,提问作者Rory McCrossan
相关产品推荐
相关产品推荐

