Asp-Net-Core Development Notes: API Version Management
For Web API applications, as time passes and requirements increase or change, the API will inevitably encounter the need for upgrades. In fact, Web API applications should consider API versioning from the moment they are created. Changes in business logic, addition of features, removal or renaming of endpoints, modifications to endpoint parameters, and additions, deletions, or changes to entity properties will all alter the API's functionality, thereby leading to version changes.
目录9 个章节
Preface
For Web API applications, as time progresses and requirements increase or change, APIs will inevitably encounter the need for upgrades. In fact, Web API applications should consider API versioning from the very beginning of their creation. Business adjustments, feature additions, endpoint removals and renaming, changes in endpoint parameters, as well as adding, removing, or modifying entity properties can all alter API functionality, thus resulting in version changes.
Most existing resources use the Microsoft.AspNetCore.Mvc.Versioning package, but upon actual use, I found that this package has long been discontinued. Microsoft's official documentation also seems to lack information on this. However, the package's NuGet page indicates it has been replaced by the new Asp.Versioning.Mvc package. It turns out Microsoft's renaming department has been at it again—my apologies for the oversight! 😂
Fortunately, this new package has detailed documentation on GitHub, but the speed of renaming is quite astonishing. In order to implement this functionality, I took quite a few detours. 😂
OK, this article is based on .Net6.0 and uses AspNetCore WebApi as an example to introduce the process of incorporating API version management.
Basics
There are two ways to specify a version: you can either use the [ApiVersion] attribute or the version convention approach. Once different versions of an API interface are defined, clients can access a specific version of the API through several methods as follows:
- Using the URL path, e.g.,
api/v1.0/values - Using a query string, e.g.,
api/values?api-version=1.0 - Using a custom HTTP header
- Using a Media Type parameter, e.g.,
Accept: application/json;v=2.0
The default method in ASP.NET Core MVC is using a query string, with the parameter name api-version. The specific method to use is determined by the server (configured using the ApiVersionReader property introduced below), and it can be one of the methods or multiple methods used simultaneously.
The API version format consists of a major version number and a minor version number, and it can also include two optional parts: a version group and a status.
[Version Group.]<Major>.<Minor>[-Status]<Version Group>[<Major>[.Minor]][-Status]
The version group format is YYYY-MM-DD and serves to logically group API interfaces, while the status indicates the current state of the version, such as Alpha, Beta, and RC. Here are some common version formats:
/api/foo?api-version=1.0/api/foo?api-version=2.0-Alpha/api/foo?api-version=2015-05-01.3.0/api/v1/foo/api/v2.0-Alpha/foo/api/v2015-05-01.3.0/foo
This article uses the format /api/v1/foo.
Installing Dependencies
You need to install these two NuGet packages:
- Asp.Versioning.Mvc
- Asp.Versioning.Mvc.ApiExplorer
Register Services
Edit the Program.cs file
builder.Services.AddApiVersioning(options => {
options.DefaultApiVersion = new ApiVersion(1, 0);
options.AssumeDefaultVersionWhenUnspecified = true;
options.ReportApiVersions = true;
options.ApiVersionReader = ApiVersionReader.Combine(
new UrlSegmentApiVersionReader(),
new HeaderApiVersionReader("x-api-version"),
new MediaTypeApiVersionReader("ver")
);
})
.AddMvc()
.AddApiExplorer(options => {
options.GroupNameFormat = "'v'VVV";
options.SubstituteApiVersionInUrl = true;
});
The code above does the following:
DefaultApiVersionsets the default version to 1.0.AssumeDefaultVersionWhenUnspecifieduses the default version when no version is specified.ReportApiVersionsadds available API versions to the response headers.ApiVersionReaderdefines that API version information can be retrieved from three places: the URL and two request headers.GroupNameFormatspecifies the format for version names, see the table below for details.SubstituteApiVersionInUrlis set to true because URL-based versioning is being used.
API Version Format Strings
In this article, I use the 'v'VVV format
| Format Specifier | Description | Examples |
|---|---|---|
| F | Full API version as [group version][.major[.minor]][-status] | 2017-05-01.1-RC -> 2017-05-01.1-RC |
| FF | Full API version with optional minor version as [group version][.major[.minor,0]][-status] | 2017-05-01.1-RC -> 2017-05-01.1.0-RC |
| G | Group version as yyyy-MM-dd | 2017-05-01.1-RC -> 2017-05-01 |
| GG | Group version as yyyy-MM-dd with status | 2017-05-01.1-RC -> 2017-05-01-RC |
| y | Group version year from 0 to 99 | 2001-05-01.1-RC -> 1 |
| yy | Group version year from 00 to 99 | 2001-05-01.1-RC -> 01 |
| yyy | Group version year with a minimum of three digits | 2017-05-01.1-RC -> 017 |
| yyyy | Group version year as a four-digit number | 2017-05-01.1-RC -> 2017 |
| M | Group version month from 1 through 12 | 2001-05-01.1-RC -> 5 |
| MM | Group version month from 01 through 12 | 2001-05-01.1-RC -> 05 |
| MMM | Group version abbreviated name of the month | 2001-06-01.1-RC -> Jun |
| MMMM | Group version full name of the month | 2001-06-01.1-RC -> June |
| d | Group version day of the month, from 1 through 31 | 2001-05-01.1-RC -> 1 |
| dd | Group version day of the month, from 01 through 31 | 2001-05-01.1-RC -> 01 |
| ddd | Group version abbreviated name of the day of the week | 2001-05-01.1-RC -> Mon |
| dddd | Group version full name of the day of the week | 2001-05-01.1-RC -> Monday |
| v | Minor version | 2001-05-01.1-RC -> 1 1.1 -> 1 |
| V | Major version | 1.0-RC -> 1 2.0 -> 2 |
| VV | Major and minor version | 1-RC -> 1 1.1-RC -> 1.1 1.1 -> 1.1 |
| VVV | Major, optional minor version, and status | 1-RC -> 1-RC 1.1 -> 1.1 |
| VVVV | Major, minor version, and status | 1-RC -> 1.0-RC 1.1 -> 1.1 1 -> 1.0 |
| S | Status | 1.0-Beta -> Beta |
| p | Padded minor version with default of two digits | 1.1 -> 01 1 -> 00 |
| p[n] | Padded minor version with N digits | p2: 1.1 -> 01 p3: 1.1 -> 001 |
| P | Padded major version with default of two digits | 2.1 -> 02 2 -> 02 |
| P[n] | Padded major version with N digits | P2: 2.1 -> 02 P3: 2.1 -> 002 |
| PP | Padded major and minor version with a default of two digits | 2.1 -> 02.01 2 -> 02.00 |
| PPP | Padded major, optional minor version, and status with a default of two digits | 1-RC -> 01-RC 1.1-RC -> 01.01-RC |
| PPPP | Padded major, minor version, and status with a default of two digits | 1-RC -> 01.00-RC 1.1-RC -> 01.01-RC |
Setting Up API Versions
The example interface has two versions:
- /api/v1/demo/test
- /api/v2/demo/test
Create two directories under the Controller, v1 and v2, then create a Controller in each respectively.
Here's the code for Controllers/v1/DemoController.cs:
[Route("api/v{version:apiVersion}/[controller]")]
[ApiVersion(1.0)]
[ApiController]
public class DemoController : ControllerBase {
[HttpGet("[action]")]
public ApiResponse Test() {
return ApiResponse.Ok("version=1.0");
}
}
The other version of the interface Controllers/v2/DemoController.cs:
[Route("api/v{version:apiVersion}/[controller]")]
[ApiVersion(2.0)]
[ApiController]
public class DemoController : ControllerBase {
[HttpGet("[action]")]
public ApiResponse Test() {
return ApiResponse.Ok("version=2.0");
}
}
As you can see, to distinguish between different versions of the interface, you only need to add the [ApiVersion(2.0)] attribute.
Since I want to use the URL to select different versions of the interface, the route needs to be configured as "api/v{version:apiVersion}/[controller]".
If you don't include the version number in the URL, you can also pass it using a request parameter, for example, /api/demo/test?api-version=1.0.
This can be configured in the ApiVersionReader attribute.
Configuring Swagger
Swagger has essentially become the standard for API documentation, but I've noticed many articles overlook this part. (Fortunately, the official documentation didn't forget.)
First, create a configuration class.
public class ConfigureSwaggerOptions : IConfigureOptions<SwaggerGenOptions> {
readonly IApiVersionDescriptionProvider provider;
public ConfigureSwaggerOptions(IApiVersionDescriptionProvider provider) =>
this.provider = provider;
public void Configure(SwaggerGenOptions options) {
foreach (var description in provider.ApiVersionDescriptions) {
options.SwaggerDoc(
description.GroupName,
new OpenApiInfo() {
Title = $"Example API {description.ApiVersion}",
Version = description.ApiVersion.ToString(),
});
}
}
}
Registering Services
builder.Services.AddTransient<IConfigureOptions<SwaggerGenOptions>, ConfigureSwaggerOptions>();
Configuring Middleware
app.UseSwagger();
app.UseSwaggerUI(options => {
foreach (var description in app.DescribeApiVersions()) {
var url = $"/swagger/{description.GroupName}/swagger.json";
var name = description.GroupName.ToUpperInvariant();
options.SwaggerEndpoint(url, name);
}
});
Effects & Testing
Done. Access the swagger documentation, and in the upper right corner, the API grouping (接口分组) shows different versions.

Request https://localhost:7053/api/v1/Demo/Test
The API returns:
{
"statusCode": 200,
"successful": true,
"message": "version=1.0",
"data": null,
"errorData": null
}
Request https://localhost:7053/api/v2/Demo/Test
The API returns:
{
"statusCode": 200,
"successful": true,
"message": "version=2.0",
"data": null,
"errorData": null
}
Not bad~ 😃
References
_EOF_
Asp-Net-Core Development Notes: API Version Management
- 本文作者:
- 程序设计实验室
- 版权声明:
- 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
相关文章
Asp-Net-Core开发笔记:快速在已有项目中引入EFCore
很多项目一开始选型的时候没有选择EFCore,不过EFCore确实好用,也许由于种种原因后面还是需要用到,这时候引入EFCore也很方便。 本文以 StarBlog 为例,StarBlog 目前使用的 ORM 是 FreeSQL ,引入 EFCore 对我来说最大的好处是支持多个数据库,如果是 FreeSQL 的话,服务注册的时候是单例模式,只能连接一个数据库,如果需要使用 FreeSQL 同时
2023-10-08 · Asp-Net-Core开发笔记 · 约 1 分钟
Asp-Net-Core开发笔记:使用RateLimit中间件实现接口限流
前言 最近一直在忙(2月份沉迷steam,3月开始工作各种忙),好久没更新博客了,不过也积累了一些,忙里偷闲记录一下。 这个需求是这样的,我之前做了个工单系统,现在要对登录、注册、发起工单这些功能做限流,不能让用户请求太频繁。 从 .Net7 开始,已经有内置的限流功能了,但目前我们的项目还在使用 .Net6 LTS 版本,下一个 LTS 没发布之前,暂时不考虑使用 .Net7 这种非 LTS 版
2023-03-20 · Asp-Net-Core开发笔记 · 约 1 分钟
Asp-Net-Core开发笔记:使用ActionFilterAttribute实现非侵入式的参数校验
用ActionFilterAttribute搞非侵入校验,省得重复写代码。支持Query、Body、Route、Header,HttpContext共享数据,但OAuth2.0边学边做,项目乱糟糟,功能凑合。
2024-05-17 · Asp-Net-Core开发笔记 · 约 1 分钟
Asp-Net-Core开发笔记:使用alpine镜像并加入健康检查
用alpine镜像?不过是把体积砍半,健康检查加个路径就完事。wget自带,何必装curl,真是多余。发布选linux-musl-x64,不然别想跑。
2024-04-19 · Asp-Net-Core开发笔记 · 约 1 分钟
讨论
共 0 条加载评论中…