前言
對(duì)于Web API應(yīng)用程序而言,隨著時(shí)間的推移以及需求的增加或改變,API必然會(huì)遇到升級(jí)的需求。事實(shí)上,Web API應(yīng)用程序應(yīng)該從創(chuàng)建時(shí)就考慮到API版本的問(wèn)題。業(yè)務(wù)的調(diào)整、功能的增加、接口的移除與改名、接口參數(shù)變動(dòng)、實(shí)體屬性的添加、刪除和更改等都會(huì)改變API的功能,從而帶來(lái)版本的變更。
現(xiàn)有的資料大部分是使用 Microsoft.AspNetCore.Mvc.Versioning
這個(gè)包,但我實(shí)際使用的時(shí)候發(fā)現(xiàn)這個(gè)包早就不更新了,微軟官方文檔好像也沒(méi)有這部分介紹,不過(guò)在這個(gè)包的nuget主頁(yè)上有說(shuō)已經(jīng)換成新的 Asp.Versioning.Mvc
包,原來(lái)是微軟改名部發(fā)力了,失敬失敬~ ??
好在這個(gè)新的包在Github上有很詳細(xì)的文檔,但這改名速度實(shí)在是猛,為了實(shí)現(xiàn)這個(gè)功能,我走了不少?gòu)澛贰??
OK,本文基于 .Net6.0,以 AspNetCore WebApi
為例,介紹引入API版本管理的過(guò)程。
基礎(chǔ)
指定版本的方法有兩種,既可以使用[ApiVersion]特性,也可以使用版本約定方式。當(dāng)定義了不同版本的API接口后,客戶端可以通過(guò)如下多種方式來(lái)訪問(wèn)某一版本的API。
- 使用URL路徑,如 api/v1.0/values
- 使用查詢字符串,如 api/values?api-version=1.0
- 使用HTTP自定義消息頭
- 使用媒體類型(Media Type)參數(shù),如 Accept: application/json;v=2.0
ASP.NET Core MVC默認(rèn)的方式是使用查詢字符串,查詢字符串使用的參數(shù)名為api-version。具體使用哪種方式由服務(wù)端指定(用下面介紹的 ApiVersionReader
屬性來(lái)配置),既可以使用其中的一種,也可以同時(shí)使用多種不同的方式。
API版本的格式由主版本號(hào)與次版本號(hào)組成,此外還可以包含可選的兩部分:版本組和狀態(tài)。
[Version Group.]<Major>.<Minor>[-Status]
<Version Group>[<Major>[.Minor]][-Status]
版本組的格式為YYYY-MM-DD,它能夠?qū)PI接口起到邏輯分組的作用,狀態(tài)則能夠標(biāo)識(shí)當(dāng)前版本的狀況,如Alpha、Beta和RC等。以下是常見(jiàn)的版本格式:
- /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
本文采用 /api/v1/foo
形式
安裝依賴
需要安裝這倆nuget包
- Asp.Versioning.Mvc
- Asp.Versioning.Mvc.ApiExplorer
注冊(cè)服務(wù)
編輯 Program.cs
文件
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;
});
以上代碼做了這些事:
-
DefaultApiVersion
設(shè)置默認(rèn)版本為1.0 -
AssumeDefaultVersionWhenUnspecified
沒(méi)有指定版本時(shí),使用默認(rèn)版本 -
ReportApiVersions
在響應(yīng)頭里加上可用的接口版本 -
ApiVersionReader
定義了可以從三個(gè)地方獲取接口版本信息,URL里和倆請(qǐng)求頭 -
GroupNameFormat
指定了版本名稱格式,詳見(jiàn)下表 -
SubstituteApiVersionInUrl
因?yàn)橐褂肬RL指定版本,所以這里設(shè)置為true
API Version Format Strings
本文中我使用的是 'v'VVV
的格式
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 |
設(shè)置API版本
例子接口有倆版本
- /api/v1/demo/test
- /api/v2/demo/test
在 Controller 下創(chuàng)建倆目錄,v1 和 v2,然后分別創(chuàng)建Controller
上代碼 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");
}
}
另一個(gè)版本的接口 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");
}
}
可以看到要區(qū)分不同版本的接口,只需要添加 [ApiVersion(2.0)]
特性即可。
因?yàn)槲乙褂肬RL來(lái)選擇不同版本的接口,所以要把路由配置為 "api/v{version:apiVersion}/[controller]"
如果不把版本號(hào)寫在URL里,也可以用請(qǐng)求參數(shù)傳遞,比如 /api/demo/test?api-version=1.0
這些可以在 ApiVersionReader
屬性配置
配置Swagger
swagger基本已經(jīng)是接口文檔的標(biāo)準(zhǔn)了,但我發(fā)現(xiàn)很多文章都沒(méi)有介紹swagger這塊。(還好官方文檔沒(méi)有忘記)
首先創(chuàng)建一個(gè)配置類
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(),
});
}
}
}
注冊(cè)服務(wù)
builder.Services.AddTransient<IConfigureOptions<SwaggerGenOptions>, ConfigureSwaggerOptions>();
配置中間件
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);
}
});
效果 & 測(cè)試
搞定,訪問(wèn)swagger文檔,在右上角接口分組可以看到不同版本
請(qǐng)求 https://localhost:7053/api/v1/Demo/Test
接口返回文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-473300.html
{
"statusCode": 200,
"successful": true,
"message": "version=1.0",
"data": null,
"errorData": null
}
請(qǐng)求 https://localhost:7053/api/v2/Demo/Test
接口返回
{
"statusCode": 200,
"successful": true,
"message": "version=2.0",
"data": null,
"errorData": null
}
不錯(cuò)~ ??文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-473300.html
參考資料
- https://github.com/dotnet/aspnet-api-versioning/wiki
到了這里,關(guān)于Asp-Net-Core開發(fā)筆記:API版本管理的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!