.net webapi 实现 接口版本控制并打通swagger支持
我们在开发 webapi 项目时如果遇到 api 接口需要同时支持多个版本的时候,比如接口修改了入参之后但是又希望支持老版本的前端(这里的前端可能是网页,可能是app,小程序 等等)进行调用,这种情况常见于 app,毕竟网页前端我们可以主动控制发布,只要统一发布后所有人的浏览器下一次访问网页时都会重新加载到最新版的代码,但是像 app 则无法保证用户一定会第一时间升级更新最新版的app,所以往往需要 api接口能够同时保持多个版本的逻辑,同支持新老版本的调用端app进行调用。
针对上面的描述举一个例子:
比如一个创建用户的接口,api/user/createuser
如果我们这个时候对该接口的入参和返回参数修改之后,但是又希望原本的 api/user/createuser 接口逻辑也可以正常运行,常见的做法有以下几种:
- 修改接口名称,将新的创建用户接口地址定义为 api/user/newcreateuser
- url传入版本标记,将新的创建用户接口地址定义为 api/user/createuser?api-version=2
- header传入版本标记,通过校验 header 中的 api-version 字段的值,用来区分调用不同版本的api
第一种方式的缺陷很明显,当接口版本多了之后接口的地址会定义很乱,本文主要讲解后面两种方法,如何在 asp.net webapi 项目中优雅的使用 header 或者 query 传入 版本标记,用来支持api的多个版本逻辑共存,并且扩展 Swagger 来实现 SwaggerUI 对于 api-version 的支持。
截至本文撰写时间,最新的 .net 版本为 .net6 ,本文中的所有示例也是基于 .net 6 来构建的。
首先创建一个 asp.net webapi 项目,本文使用 vs2022 直接创建 asp.net webapi 项目
项目创建好之后安装如下几个nuget包:
Swashbuckle.AspNetCore
Microsoft.AspNetCore.Mvc.Versioning.ApiExplorer
注册 api 版本控制服务
#region 注册 api 版本控制
builder.Services.AddApiVersioning(options =>
{
//通过Header向客户端通报支持的版本
options.ReportApiVersions = true;
//允许不加版本标记直接调用接口
options.AssumeDefaultVersionWhenUnspecified = true;
//接口默认版本
//options.DefaultApiVersion = new ApiVersion(1, 0);
//如果未加版本标记默认以当前最高版本进行处理
options.ApiVersionSelector = new CurrentImplementationApiVersionSelector(options);
//配置为从 Header 传入 api-version
options.ApiVersionReader = new HeaderApiVersionReader("api-version");
//配置为从 Query 传入 api-version
//options.ApiVersionReader = new QueryStringApiVersionReader("api-version");
});
builder.Services.AddVersionedApiExplorer(options =>
{
options.GroupNameFormat = "'v'VVV";
options.SubstituteApiVersionInUrl = true;
});
#endregion
这里我们可以选择 api-version 版本标记的传入方式是从 url query 传递还是从 http header 传递。
移除项目默认的 swagger 配置
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(); // Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
采用如下 swagger 配置
#region 注册 Swagger
builder.Services.AddTransient<IConfigureOptions<SwaggerGenOptions>, SwaggerConfigureOptions>();
builder.Services.AddSwaggerGen(options =>
{
options.OperationFilter<SwaggerOperationFilter>();
options.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, $"{typeof(Program).Assembly.GetName().Name}.xml"), true);
});
#endregion
其中用到了两个自定义的类 SwaggerConfigureOptions 和 SwaggerOperationFilter ,
SwaggerConfigureOptions 是一个自定义的 Swagger 配置方法,主要用于根据 api 控制器上的描述用来循环添加不同版本的 SwaggerDoc;
SwaggerOperationFilter 是一个自定义过滤器主要实现SwaggerUI 的版本参数 api-version 必填验证和标记过期的 api 的功能,具体内容如下
SwaggerConfigureOptions .cs
/// <summary>
/// 配置swagger生成选项。
/// </summary>
public class SwaggerConfigureOptions : IConfigureOptions<SwaggerGenOptions>
{
readonly IApiVersionDescriptionProvider provider; public SwaggerConfigureOptions(IApiVersionDescriptionProvider provider) => this.provider = provider; public void Configure(SwaggerGenOptions options)
{
foreach (var description in provider.ApiVersionDescriptions)
{
options.SwaggerDoc(description.GroupName, CreateInfoForApiVersion(description)); var modelPrefix = Assembly.GetEntryAssembly()?.GetName().Name + ".Models.";
var versionPrefix = description.GroupName + ".";
options.SchemaGeneratorOptions = new SchemaGeneratorOptions { SchemaIdSelector = type => (type.ToString()[(type.ToString().IndexOf("Models.") + 7)..]).Replace(modelPrefix, "").Replace(versionPrefix, "").Replace("`1", "").Replace("+", ".") };
}
} static OpenApiInfo CreateInfoForApiVersion(ApiVersionDescription description)
{
var info = new OpenApiInfo()
{
Title = Assembly.GetEntryAssembly()?.GetName().Name,
Version = "v" + description.ApiVersion.ToString(),
//Description = "",
//Contact = new OpenApiContact() { Name = "", Email = "" }
}; if (description.IsDeprecated)
{
info.Description += "此 Api " + info.Version + " 版本已弃用,请尽快升级新版";
} return info;
}
}
SwaggerOperationFilter.cs
/// <summary>
/// swagger 集成多版本api自定义设置
/// </summary>
public class SwaggerOperationFilter : IOperationFilter
{ public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
var apiDescription = context.ApiDescription; //判断接口遗弃状态,对接口进行标记调整
operation.Deprecated |= apiDescription.IsDeprecated(); if (operation.Parameters == null)
{
return;
} //为 api-version 参数添加必填验证
foreach (var parameter in operation.Parameters)
{
var description = apiDescription.ParameterDescriptions.First(p => p.Name == parameter.Name); if (parameter.Description == null)
{
parameter.Description = description.ModelMetadata?.Description;
} if (parameter.Schema.Default == null && description.DefaultValue != null)
{
parameter.Schema.Default = new OpenApiString(description.DefaultValue.ToString());
} parameter.Required |= description.IsRequired;
}
}
}
这些都配置完成之后,开始对 控制模块进行调整
为了方便代码的版本区分,所以我这里在 Controllers 下按照版本建立的独立的文件夹 v1 和 v2
然后在 v1 和 v2 的文件夹下防止了对于的 Controllers,如下图的结构

然后只要在对应文件夹下的控制器头部加入版本标记
[ApiVersion("1")] [ApiVersion("2")] [ApiVersion("......")]
如下图的两个控制器

这样就配置好了两个版本的 UserController 具体控制器内部的代码可以不同,然后运行 项目观察 Swagger UI 就会发现如下图:

可以通过 SwaggerUI 右上角去切换各个版本的 SwaggerDoc

点击单个接口的 Try it out 时接口这边也同样会出现一个 api-version 的字段,因为我们这边是配置的从 Header 传入该参数所以从界面中可以看出该字段是从 Header 传递的,如果想要从 url 传递,主要调整上面 注册 api 版本控制服务 那边的设置为从 Query 传入即可。
至此基础的 api 版本控制逻辑就算完成了。
下面衍生讲解一下如果 项目中有部分 api 控制器并不需要版本控制,是全局通用的如何处理,有时候我们一个项目中总会存在一些基础的 api 是基本不会变的,如果每次 api 版本升级都把所有的 控制器都全部升级显然太过繁琐了,所以我们可以把一些全局通用的控制器单独标记出来。
只要在这些控制器头部添加 [ApiVersionNeutral] 标记即可,添加了 [ApiVersionNeutral] 标记的控制器则表明该控制器退出了版本控制逻辑,无论 app 前端传入的版本号的是多少,都可以正常进入该控制的逻辑。如下
[ApiVersionNeutral]
[ApiController]
[Route("api/[controller]")]
public class FileController : ControllerBase
{ }
还有一种就是当我们的 api 版本升级之后,我们希望标记某个 api 已经是弃用的,则可以使用 Deprecated 来表示该版本的 api 已经淘汰。
[ApiVersion("1", Deprecated = true)]
[ApiController]
[Route("api/[controller]")]
public class UserController : ControllerBase
{
[HttpPost("CreateUser")]
public void CreateUser(DtoCreateUser createUser)
{
//内部注册逻辑此处省略
}
}
添加淘汰标记之后运行 SwaggerUI 就会出现下图的样式

通过 SwaggerDoc 就可以很明确的看出 v1 版本的 api 已经被淘汰了。
至此 关于asp.net core webapi 中 api 版本控制的基本操作就讲解完了,有任何不明白的,可以在文章下面评论或者私信我,欢迎大家积极的讨论交流。
.net webapi 实现 接口版本控制并打通swagger支持的更多相关文章
- 使用Swashbuckle.AspNetCore生成.NetCore WEBAPI的接口文档
一.问题 使用Swashbuckle.AspNetCore生成.NetCore WEBAPI的接口文档的方法 二.解决方案 参考文章:https://docs.microsoft.com/zh-cn/ ...
- Net Core WebApi几种版本控制对比
Net Core WebApi几种版本控制对比 一.版本控制的好处: (1)有助于及时推出功能, 而不会破坏现有系统. (2)它还可以帮助为选定的客户提供额外的功能. API 版本控制可以采用不同的方 ...
- Asp.Net WebAPI配置接口返回数据类型为Json格式
Asp.Net WebAPI配置接口返回数据类型为Json格式 一.默认情况下WebApi 对于没有指定请求数据类型类型的请求,返回数据类型为Xml格式 例如:从浏览器直接输入地址,或者默认的XM ...
- c# WebApi之接口返回类型详解
c# WebApi之接口返回类型详解 https://blog.csdn.net/lwpoor123/article/details/78644998
- ASP.NET WebApi服务接口如何防止重复请求实现HTTP幂等性
一.背景描述与课程介绍 明人不说暗话,跟着阿笨一起玩WebApi.在我们平时开发项目中可能会出现下面这些情况; 1).由于用户误操作,多次点击网页表单提交按钮.由于网速等原因造成页面卡顿,用户重复刷新 ...
- SpringBoot系统列 5 - 接口版本控制、SpringBoot FreeMarker模板引擎
接着上篇博客的代码继续写 1.接口版本控制 一个系统上线后会不断迭代更新,需求也会不断变化,有可能接口的参数也会发生变化,如果在原有的参数上直接修改,可能会影响线上系统的正常运行,这时我们就需要设置不 ...
- 接口文档神器Swagger(下篇)
本文来自网易云社区 作者:李哲 二.Swagger-springmvc原理解析 上面介绍了如何将springmvc和springboot与swagger结合,通过简单配置生成接口文档,以及介绍了swa ...
- 接口文档神器Swagger(上篇)
本文来自网易云社区 作者:李哲 接口文档管理一直是一个让人头疼的问题,伴随着各种接口文档管理平台涌现,如阿里开源的rap,ShowDoc,sosoapi,等等(网上能找到很多这种管理平台,包括我们自己 ...
- .Net Core 分布式微服务框架 - Jimu 添加 Swagger 支持
系列文章 .Net Core 分布式微服务框架介绍 - Jimu .Net Core 分布式微服务框架 - Jimu 添加 Swagger 支持 一.前言 最近有空就优化 Jimu (一个基于.Net ...
随机推荐
- web服务报错类型
401:无权限(HttpStatus.UNAUTHORIZED) 404:页面找不到 405:不支持get/post请求,如只支持get请求但传了post请求 400:请求格式错误,如不为null但传 ...
- XCTF练习题---CRYPTO---wtc_rsa_bbq
XCTF练习题---CRYPTO---wtc_rsa_bbq flag:flag{how_d0_you_7urn_this_0n?} 解题步骤: 1.观察题目,下载附件 2.下载后是一个文件,不清楚格 ...
- 2003031121-浦娟-python数据分析第四周作业-第二次作业
项目 内容 课程班级博客链接 20级数据班(本) 作业链接 Python第四周作业第二次作业 博客名称 2003031121-浦娟-python数据分析第四周作业-matolotlib的应用 要求 每 ...
- 浅谈 Linux IO
公众号关注 「开源Linux」 回复「学习」,有我为您特别筛选的学习资料~ 来源于:360云计算 1 前言 Linux IO是文件存储的基础.本文参考了网上博主的一些文章,主要总结了LinuxIO的基 ...
- 论文阅读 DyREP:Learning Representations Over Dynamic Graphs
5 DyREP:Learning Representations Over Dynamic Graphs link:https://scholar.google.com/scholar_url?url ...
- 438. Find All Anagrams in a String - LeetCode
Question 438. Find All Anagrams in a String Solution 题目大意:给两个字符串,s和p,求p在s中出现的位置,p串中的字符无序,ab=ba 思路:起初 ...
- 206. Reverse Linked List - LeetCode
Question 206. Reverse Linked List Solution 题目大意:对一个链表进行反转 思路: Java实现: public ListNode reverseList(Li ...
- 盘点微信小程序跨页面传值的若干方式
直接给大家上干货 1.跳转页面传递参数 pageA.wxml <button type="primary" bindtap="jumpTo">点击跳 ...
- POJ1821 Fence 题解报告
传送门 1 题目描述 A team of $k (1 <= K <= 100) $workers should paint a fence which contains \(N (1 &l ...
- .NET 6.0.6 和 .NET Core 3.1.26、Visual Studio 2022 17.2 和 17.3 Preview 2 和 .NET 7.0 Preview 5 同时发布
Microsoft 昨天发布了适用于 .NET 6.0.6 和 .NET Core 3.1.26.NuGet.Visual Studio 2019 和 Visual Studio 2022 17.2 ...