天天看點

Asp-Net-Core開發筆記:API版本管理

作者:opendotnet

1前言

對于Web API應用程式而言,随着時間的推移以及需求的增加或改變,API必然會遇到更新的需求。事實上,Web API應用程式應該從建立時就考慮到API版本的問題。業務的調整、功能的增加、接口的移除與改名、接口參數變動、實體屬性的添加、删除和更改等都會改變API的功能,進而帶來版本的變更。

現有的資料大部分是使用

Microsoft.AspNetCore.Mvc.Versioning

這個包,但我實際使用的時候發現這個包早就不更新了,微軟官方文檔好像也沒有這部分介紹,不過在這個包的nuget首頁上有說已經換成新的

Asp.Versioning.Mvc

包,原來是微軟改名部發力了,失敬失敬~ 😂

好在這個新的包在Github上有很詳細的文檔,但這改名速度實在是猛,為了實作這個功能,我走了不少彎路。😂

OK,本文基于 .Net6.0,以

AspNetCore WebApi

為例,介紹引入API版本管理的過程。

2基礎

指定版本的方法有兩種,既可以使用[ApiVersion]特性,也可以使用版本約定方式。當定義了不同版本的API接口後,用戶端可以通過如下多種方式來通路某一版本的API。

  • 使用URL路徑,如 api/v1.0/values
  • 使用查詢字元串,如 api/values?api-version=1.0
  • 使用HTTP自定義消息頭
  • 使用媒體類型(Media Type)參數,如 Accept: application/json;v=2.0

ASP.NET Core MVC預設的方式是使用查詢字元串,查詢字元串使用的參數名為api-version。具體使用哪種方式由服務端指定(用下面介紹的

ApiVersionReader

屬性來配置),既可以使用其中的一種,也可以同時使用多種不同的方式。

API版本的格式由主版本号與次版本号組成,此外還可以包含可選的兩部分:版本組和狀态。

  • [Version Group.]<Major>.<Minor>[-Status]

  • <Version Group>[<Major>[.Minor]][-Status]

版本組的格式為YYYY-MM-DD,它能夠對API接口起到邏輯分組的作用,狀态則能夠辨別目前版本的狀況,如Alpha、Beta和RC等。以下是常見的版本格式:

  • /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

形式

3安裝依賴

需要安裝這倆nuget包

  • Asp.Versioning.Mvc
  • Asp.Versioning.Mvc.ApiExplorer

4注冊服務

編輯

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

    設定預設版本為1.0
  • AssumeDefaultVersionWhenUnspecified

    沒有指定版本時,使用預設版本
  • ReportApiVersions

    在響應頭裡加上可用的接口版本
  • ApiVersionReader

    定義了可以從三個地方擷取接口版本資訊,URL裡和倆請求頭
  • GroupNameFormat

    指定了版本名稱格式,詳見下表
  • SubstituteApiVersionInUrl

    因為要使用URL指定版本,是以這裡設定為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

5設定API版本

例子接口有倆版本

  • /api/v1/demo/test
  • /api/v2/demo/test

在 Controller 下建立倆目錄,v1 和 v2,然後分别建立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");
 }
}
           

另一個版本的接口

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");
 }
}
           

可以看到要區分不同版本的接口,隻需要添加

[ApiVersion(2.0)]

特性即可。

因為我要使用URL來選擇不同版本的接口,是以要把路由配置為

"api/v{version:apiVersion}/[controller]"

如果不把版本号寫在URL裡,也可以用請求參數傳遞,比如

/api/demo/test?api-version=1.0

這些可以在

ApiVersionReader

屬性配置

6配置Swagger

swagger基本已經是接口文檔的标準了,但我發現很多文章都沒有介紹swagger這塊。(還好官方文檔沒有忘記)

首先建立一個配置類

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(),
 });
 }
 }
}
           

注冊服務

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);
 }
});
           

7效果 & 測試

搞定,通路swagger文檔,在右上角接口分組可以看到不同版本

Asp-Net-Core開發筆記:API版本管理

請求

https://localhost:7053/api/v1/Demo/Test

接口傳回

{
"statusCode": 200,
"successful": true,
"message": "version=1.0",
"data": ,
"errorData": 
}
           

請求

https://localhost:7053/api/v2/Demo/Test

接口傳回

{
"statusCode": 200,
"successful": true,
"message": "version=2.0",
"data": ,
"errorData": 
}
           

不錯~ 😃

8參考資料

  • https://github.com/dotnet/aspnet-api-versioning/wiki

繼續閱讀