前言

对于Web API应用程序而言,随着时间的推移以及需求的增加或改变,API必然会遇到升级的需求。事实上,Web API应用程序应该从创建时就考虑到API版本的问题。业务的调整、功能的增加、接口的移除与改名、接口参数变动、实体属性的添加、删除和更改等都会改变API的功能,从而带来版本的变更。

现有的资料大部分是使用 Microsoft.AspNetCore.Mvc.Versioning 这个包,但我实际使用的时候发现这个包早就不更新了,微软官方文档好像也没有这部分介绍,不过在这个包的nuget主页上有说已经换成新的 Asp.Versioning.Mvc 包,原来是微软改名部发力了,失敬失敬~

好在这个新的包在Github上有很详细的文档,但这改名速度实在是猛,为了实现这个功能,我走了不少弯路。

OK,本文基于 .Net6.0,以 AspNetCore WebApi 为例,介绍引入API版本管理的过程。

基础

指定版本的方法有两种,既可以使用[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 形式

安装依赖

需要安装这俩nuget包

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

注册服务

编辑 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

设置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 属性配置

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

效果 & 测试

搞定,访问swagger文档,在右上角接口分组可以看到不同版本

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

接口返回

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

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

接口返回

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

不错~

参考资料

Asp-Net-Core开发笔记:API版本管理的更多相关文章

  1. 在CentOS7 开发与部署 asp.net core app笔记

    原文:在CentOS7 开发与部署 asp.net core app笔记 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/lihongzhai/art ...

  2. 2月送书福利:ASP.NET Core开发实战

    大家都知道我有一个公众号“恰童鞋骚年”,在公众号2020年第一天发布的推文<2020年,请让我重新介绍我自己>中,我曾说到我会在2020年中每个月为所有关注“恰童鞋骚年”公众号的童鞋们送一 ...

  3. dot watch+vs code提成asp.net core开发效率

    在园子中,已经又前辈介绍过dotnet watch的用法,但是是基于asp.net core 1.0的较老版本来讲解的,在asp.net core 2.0的今天,部分用法已经不太一样,所以就再写一篇文 ...

  4. ASP.Net Core开发(踩坑)指南

    ASP.NET与ASP.NET Core很类似,但它们之间存在一些细微区别以及ASP.NET Core中新增特性的使用方法,在此之前也写过一篇简单的对比文章ASP.NET MVC应用迁移到ASP.NE ...

  5. 【视频】使用ASP.NET Core开发GraphQL服务

    GraphQL 既是一种用于 API 的查询语言也是一个满足你数据查询的运行时. GraphQL来自Facebook,它于2012年开始开发,2015年开源. GraphQL与编程语言无关,可以使用很 ...

  6. C# -- HttpWebRequest 和 HttpWebResponse 的使用 C#编写扫雷游戏 使用IIS调试ASP.NET网站程序 WCF入门教程 ASP.Net Core开发(踩坑)指南 ASP.Net Core Razor+AdminLTE 小试牛刀 webservice创建、部署和调用 .net接收post请求并把数据转为字典格式

    C# -- HttpWebRequest 和 HttpWebResponse 的使用 C# -- HttpWebRequest 和 HttpWebResponse 的使用 结合使用HttpWebReq ...

  7. dotnet watch+vs code提升asp.net core开发效率

    在园子中,已经又前辈介绍过dotnet watch的用法,但是是基于asp.net core 1.0的较老版本来讲解的,在asp.net core 2.0的今天,部分用法已经不太一样,所以就再写一篇文 ...

  8. angular4和asp.net core 2 web api

    angular4和asp.net core 2 web api 这是一篇学习笔记. angular 5 正式版都快出了, 不过主要是性能升级. 我认为angular 4还是很适合企业的, 就像.net ...

  9. 基于ASP.Net Core开发的一套通用后台框架

    基于ASP.Net Core开发一套通用后台框架 写在前面 这是本人在学习的过程中搭建学习的框架,如果对你有所帮助那再好不过.如果您有发现错误,请告知我,我会第一时间修改. 知其然,知其所以然,并非重 ...

  10. ASP.NET Core WebApi构建API接口服务实战演练

    一.ASP.NET Core WebApi课程介绍 人生苦短,我用.NET Core!提到Api接口,一般会想到以前用到的WebService和WCF服务,这三个技术都是用来创建服务接口,只不过Web ...

随机推荐

  1. ChatGPT 设计游戏剧情 | 基于 AI 5 天创建一个农场游戏,完结篇!

    欢迎使用 AI 进行游戏开发! 在本系列中,我们将使用 AI 工具在 5 天内创建一个功能完备的农场游戏.到本系列结束时,您将了解到如何将多种 AI 工具整合到游戏开发流程中.本文将向您展示如何将 A ...

  2. flutter ui---->一些类QQ的实现

    整理一下比较有意思的类QQ的UI实现.Nothing that has meaning is easy. Easy doesn't enter into grown-up life. darken t ...

  3. 在Kubernetes(k8s)中部署 jenkins

    在Kubernetes(k8s)中部署 jenkins YAML配置文件 由于jenkins需要持久化存储,通过nfs动态供给pvc存储卷. 可以参考我之前的文档:https://cloud.tenc ...

  4. 二进制安装Kubernetes(k8s) v1.24.1 IPv4/IPv6双栈 --- Ubuntu版

    二进制安装Kubernetes(k8s) v1.24.1 IPv4/IPv6双栈 --- Ubuntu版本 Kubernetes 开源不易,帮忙点个star,谢谢了 介绍 kubernetes二进制安 ...

  5. js开发规范

    ####################### 1.缩进 [强制] 使用 4 个空格做为一个缩进层级,不允许使用 2 个空格 或 tab 字符. [强制] switch中缩进2个空格 [强制] 要求分 ...

  6. 简单的cs修改器

    目录 各个函数解析 main() GetPid() 无限子弹 无限血 无限金币 Patch() 无僵直 稳定射击 Depatch1 手枪连发 Depatch 源代码部分 各个函数解析 这是我根据b站上 ...

  7. PHP安全有帮助的一些函数

    安全是编程非常重要的一个方面.在任何一种编程语言中,都提供了许多的函数或者模块来确保程序的安全性.在现代网站应用中,经常要获取来自世界各地用户的输入,但是,我们都知道"永远不能相信那些用户输 ...

  8. Redis分布式锁这样用,有坑?

    背景 在微服务项目中,大家都会去使用到分布式锁,一般也是使用Redis去实现,使用RedisTemplate.Redisson.RedisLockRegistry都行,公司的项目中,使用的是Redis ...

  9. 介绍一个.Net远程日志组件

    对于软件开发的阶段和正式运行阶段,我们都需要查看日志来诊断出现的问题.不过,在查看日志时需要登录服务器,找到特定的日志文件,再查看其中的内容,这显然不是很方便. 为了解决这个问题,我们可以使用远程日志 ...

  10. 计算机常用的快捷键以及常用的Dos命令

    计算机的常用快捷键有哪些? 今天我重温了Java基础的课程,计算机的快捷键大家肯定不陌生.   计算机的常用快捷键 ctrl+c 复制 ctrl+v 粘贴 ctrl+s 保存 ctrl+x 剪切 ct ...