[ApiVersion("1.0")] 单独使用无效,因仅是元数据;必须配合 AddApiVersioning() 注册、UrlSegmentApiVersionReader 配置、含 {version:apiVersion} 的路由模板及控制器多版本显式声明才能生效。

只加 [ApiVersion("1.0")] 不会生效——它只是元数据,不参与路由匹配,请求直接 404 或 fallback 到无版本逻辑。
为什么 [ApiVersion] 单独使用总是 404
ASP.NET Core 不会自动把 /api/v1/users 映射到标了 [ApiVersion("1.0")] 的控制器。这个特性只声明“支持 v1”,但没告诉路由系统“v1 对应哪段 URL”。真正驱动匹配的是路由模板里的 {version:apiVersion} 占位符,而该占位符只有在注册 AddApiVersioning() 后才被识别为合法约束。
- 漏掉
services.AddApiVersioning():所有版本逻辑静默失效,请求走默认 MVC 路由,可能 404 或命中错误控制器 - 路由写成
"api/[controller]"或硬编码"api/v1/[controller]":前者没传版本,后者让框架无法提取变量,Microsoft.AspNetCore.Mvc.Versioning根本不介入 -
{version:apiVersion}拼错(如{version:Version}或{v:apiVersion}):MVC 认不出这是版本约束,返回 404 而非 400 - 同时注册
"api/[controller]"和"api/v{version:apiVersion}/[controller]":前者贪婪匹配全部请求,后者永远不触发
AddApiVersioning() 必须配齐的三项关键配置
缺任意一项,都可能表现为“版本没起作用”:比如不传版本时 400、Swagger 文档里看不到 v2、响应头没有 api-supported-versions。
-
AssumeDefaultVersionWhenUnspecified = true:否则客户端不带任何版本标识(如直接 GET/api/users)会直接 400,而不是走默认 v1 -
ReportApiVersions = true:让响应头带上api-supported-versions和api-deprecated-versions,前端或网关可据此做兼容判断;不设就等于放弃运行时版本发现能力 -
ApiVersionReader = new UrlSegmentApiVersionReader():明确告诉框架“从 URL 路径段读版本”,不能依赖默认值;若还想支持?api-version=2.0,得改成new CompositeApiVersionReader(new UrlSegmentApiVersionReader(), new QueryStringApiVersionReader("api-version"))
控制器上怎么声明多版本和子版本
一个控制器要同时支持 v1 和 v2,不是靠“自动降级”,而是显式声明 + 显式路由绑定。子版本(如 v1.1)也不能靠模糊匹配,必须用 [MapToApiVersion] 精确指定。
- 控制器上必须同时有
[ApiVersion("1.0")]和[ApiVersion("2.0")]才能响应两个版本;只写一个,另一个版本的请求不会被注册进路由表 - 必须配合
[Route("api/v{version:apiVersion}/[controller]")],且该路由模板需在MapControllers()前启用 -
[MapToApiVersion("1.1")]只在控制器已声明[ApiVersion("1.1")]的前提下才起效;如果控制器只标了 v1.0,却给某个方法加[MapToApiVersion("1.1")],这个方法在 v1.1 下根本不可达 - 不同版本的同名控制器(如
UsersControllerv1 和 v2)可共存,但需用命名空间或类名区分(如UsersControllerV1),否则编译报错Type 'UsersController' already defines a member called 'Get'
Swagger 多版本文档生成总为空或混在一起
默认 AddSwaggerGen() 不知道哪个接口属于哪个版本,必须手动按 [ApiVersion] 属性筛选文档。否则所有接口都塞进同一个 v1 文档里,或者干脆漏掉高版本。
- 注册多个
SwaggerDoc()时,docName(如"v1")必须和[ApiVersion("1.0")]中的版本字符串能对上(建议统一用"v1"、"v2"这种字符串) -
DocInclusionPredicate必须检查控制器类型上的ApiVersionAttribute,不能只看 Action 方法——因为版本是按 Controller 维度声明的 - 必须确保
SwaggerGen配置中调用了ResolveConflictingActions(apiDescriptions => apiDescriptions.First()),否则同名 Action 在多版本下会冲突报错
最容易被忽略的是:[ApiVersion] 和 [Route] 必须成对出现,且 Route 模板中的 {version:apiVersion} 是唯一被框架识别的版本提取机制——拼错一个字符,整个版本控制链就断了。


















