Dotnet Core多版本API共存的优雅实现

API升级,新旧版本的API共存,怎么管理呢?

一、前言

最近,单位APP做了升级,同步的,API也做了升级。

升级过程中,出现了一点问题:API升级后,旧API也需要保留,因为有旧的APP还在使用中。

那么,API端如何作到多个版本共存呢?

二、快速的解决办法

API的露出,是在API的Route定义中实现的。看下面的例子:

[Route("api/[controller]")]
public class DemoController : ControllerBase
{[Route("demo")]public ActionResult<T> DemoFunc(){}
}

那我们知道,这个API的终结点是:/api/demo/demo。代码中[controller]是个可替换变量,编译时会替换为当前控制器的名称。

这个Route,里面的参数是个字符串,也就是说是可以随便换的。所以,对于多版本API,有个快速的办法,就是在里面做文章。

我们可以写成:

[Route("api/v1/[controller]")]
public class DemoController : ControllerBase
{[Route("demo")]public ActionResult<T> DemoFunc(){}
}

或者

[Route("api/[controller]")]
public class DemoController : ControllerBase
{[Route("v1/demo")]public ActionResult<T> DemoFunc(){}
}

这样就区分出了版本号。

当然,这样做比较LOW,因为版本号是硬编码在代码中的。而且,这个改动会影响到API的终结点,例如上面两个变化,会让终结点变为:/api/v1/demo/demo/api/demo/v1/demo。如果前端可以方便修改,也算是一个方法。但对于我们APP已经上线运行来说,这个改动无法接受。

三、优雅的解决办法

这个方案,才是今天要说的核心内容。

首先,我们需要从Nuget上引入两个库:

% dotnet add package Microsoft.AspNetCore.Mvc.Versioning
% dotnet add package Microsoft.AspNetCore.Mvc.Versioning.ApiExplorer

这两个库,Versioning用来实现API的版本控制,Versioning.ApiExplorer用来实现元数据的发现工作。

引入完成后,修改Startup.cs

public void ConfigureServices(IServiceCollection services)
{services.AddApiVersioning(options =>{options.DefaultApiVersion = new ApiVersion(1, 0);options.AssumeDefaultVersionWhenUnspecified = true;options.ReportApiVersions = true;});services.AddVersionedApiExplorer(options =>{options.GroupNameFormat = "'v'VVV";options.SubstituteApiVersionInUrl = true;});services.AddControllers();
}

就可以了。

这里面用了两个配置:AddApiVersioning,主要用来配置向前兼容,定义了如果没有带版本号的访问,会默认访问v1.0的接口。AddVersionedApiExplorer用来添加API的版本管理,并定义了版本号的格式化方式,以及兼容终结点上带版本号的方式。

到这儿,引入版本管理的工作就完成了。

使用时,就直接在控制器或方法上定义版本号:

[ApiVersion("1")]
[Route("api/[controller]")]
public class DemoController : ControllerBase
{[MapToApiVersion("2")][Route("demo")]public ActionResult<T> DemoFunc(){}
}

这里面,又是两个属性:ApiVersion定义控制器提供哪个版本的API。这个属性可以定义多个。例如,我们控制器里既有v1的API,也有v2的API,我们可以写成:

[ApiVersion("1")]
[ApiVersion("2")]
[Route("api/[controller]")]
public class DemoController : ControllerBase
{
}

MapToApiVersion是API的版本定义,定义我们这个API是哪一个版本。

方法就这么简单。其它的,微软都帮我们做好了。

那,通常我们会用Swagger来做API文档。这个方法如何跟Swagger配合呢?

四、与Swagger的配合

Swagger也来自于Nuget的引用:

% dotnet add package swashbuckle.aspnetcore

引用后,通常我们Startup.cs里的配置是这样的:

public void ConfigureServices(IServiceCollection services)
{services.AddSwaggerGen(option =>{option.SwaggerDoc("v1", new OpenApiInfo { Title = "Demo", Version = "V1" });});services.AddControllers();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{app.UseSwagger();app.UseSwaggerUI(option =>{option.SwaggerEndpoint("/swagger/v1/swagger.json", "Demo");});}

API多版本管理与Swagger配合,也有一个快速但比较LOW的方法:

public void ConfigureServices(IServiceCollection services)
{services.AddSwaggerGen(option =>{option.SwaggerDoc("v1", new OpenApiInfo { Title = "Demo", Version = "V1" });option.SwaggerDoc("v1", new OpenApiInfo { Title = "Demo", Version = "V2" });});services.AddControllers();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{app.UseSwagger();app.UseSwaggerUI(option =>{option.SwaggerEndpoint("/swagger/v1/swagger.json", "Demo V1");option.SwaggerEndpoint("/swagger/v2/swagger.json", "Demo V2");});
}

这个方法也可以快速实现,不过跟上边的情况一样,版本号是硬编码的。

其实,也有另一个比较优雅的方式,就是手动实现IConfigureOptions<SwaggerGenOptions>和过滤IOperationFilter

先看Startup.cs里:

public void ConfigureServices(IServiceCollection services)
{services.AddTransient<IConfigureOptions<SwaggerGenOptions>, ConfigureSwaggerOptions>();services.AddSwaggerGen(options => options.OperationFilter<SwaggerDefaultValues>());services.AddControllers();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{app.UseSwagger();app.UseSwaggerUI(option =>{foreach (var description in provider.ApiVersionDescriptions){c.SwaggerEndpoint($"/swagger/{description.GroupName}/swagger.json", description.GroupName.ToUpperInvariant());}});
}

这里加了两个类,第一个ConfigureSwaggerOptions

internal class ConfigureSwaggerOptions : IConfigureOptions<SwaggerGenOptions>
{private readonly IApiVersionDescriptionProvider _provider;public ConfigureSwaggerOptions(IApiVersionDescriptionProvider provider) => _provider = provider;public void Configure(SwaggerGenOptions options){foreach (var description in _provider.ApiVersionDescriptions){options.SwaggerDoc(description.GroupName, CreateInfoForApiVersion(description));}}private OpenApiInfo CreateInfoForApiVersion(ApiVersionDescription description){var info = new OpenApiInfo(){Title = "Demo API",Version = description.ApiVersion.ToString(),};if (description.IsDeprecated){info.Description += " 方法被弃用.";}return info;}
}

第二个SwaggerDefaultValues

internal class SwaggerDefaultValues : IOperationFilter
{public void Apply(OpenApiOperation operation, OperationFilterContext context){var apiDescription = context.ApiDescription;operation.Deprecated |= apiDescription.IsDeprecated();if (operation.Parameters == null)return;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;}}
}

代码不一行行解释了,都是比较简单的。

运行,进入Swagger界面,右上角Select a definition,可以选择我们定义的版本号。

今天的配套代码已上传到Github,位置在:https://github.com/humornif/Demo-Code/tree/master/0035/demo

喜欢就来个三连,让更多人因你而受益

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/306160.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java 文件监听器_java7 文件监听器

java7中的文件监听&#xff0c;主要可以监听指定目录下的文件&#xff1a;新建 删除 修改等操作。StandardWatchEventKinds.ENTRY_MODIFY,StandardWatchEventKinds.ENTRY_CREATE,StandardWatchEventKinds.ENTRY_DELETE 这三个事件 被注册到watchService 对象中&#xff0c;之后…

Beetlex之websocket/tls服务压测工具

为了方便压力测试ws服务&#xff0c;Beetlex同样提供相关工具来对ws/wss服务的性能进行测试测试。安装可以访问https://github.com/beetlex-io/TCPBenchmarks 下载最新版本工作&#xff0c;工具可以运行在linux和windows系统&#xff0c;安装完成后通过浏览器访问相关服务进行操…

php判断数组下标,php判断json或者数组格式与给定格式是否一致

开发的时候碰到个问题&#xff0c;我们技术人员规定了录数据的格式&#xff0c;例如&#xff1a;我们的json模板如下&#xff1a;{"sentence":"Martin and Kelvin went to Chengduon holiday. They are ordering in a rest aurant now but cannot read the char…

php 设置excel格式,php 操作excel文件的方法小结

用php生成excel文件的方法&#xff0c;单独就是用的php,注意一下数据的输入方法即可。一、php&#xff0c;不用COM&#xff0c;生成excel文件header("Content-type:application/vnd.ms-excel");header("Content-Disposition:filenametest.xls");echo "…

大学班里80%都去培训班,为什么我没去

背景大四刚开始&#xff0c;各大培训机构就开始到各大高校开始宣传&#xff0c;我们学校也不例外&#xff1b;当时信息与计算科学这专业在我们学校并不景气&#xff0c;有好几次听说&#xff1a;如果我们那届的就职率还不行的话&#xff0c;那很大可能将此专业拿掉&#xff1b;…

织梦手机版list.php,解决织梦一级目录作域名list.php无法跳转到手机站的问题

最近测试了www.doweb8.com/m/list.php?tid1 在手机站访问的时候&#xff0c;无法跳转到手机的模板&#xff0c;终于找到了解决了方法&#xff0c;给大家分享一下修改根目录下的m/list.php文件重新弄的代码&#xff0c; 不区分几级目录&#xff0c;理论是根据你指定的PC&#…

如何在 C# 中使用 Buffer

缓冲区 是内存中的一组字节序列&#xff0c;缓冲 是用来处理落在内存中的数据&#xff0c;.NET 缓冲 指的是处理 非托管内存 中的数据&#xff0c;用 byte[] 来表示。当你想把数据写入到内存或者你想处理非托管内存中的数据&#xff0c;可以使用 .NET 提供的 System.Buffer类&a…

php强制对齐,[强迫症福利] 使用 PHPStorm 对齐数组的键值对

用 php-cs-fixer 自动格式化&#xff0c;一个配置配合 Git 自动格式化&#xff0c;补充楼上。示例 Git Hook忽略前端的代码格式化&#xff0c;修改一下就可以用。#!/bin/bash## check PHP code syntax error and standard with phpcs# https://blog.csdn.net/xsgnzb/article/de…

.NET SDK-Style 项目(Core、Standard、.NET5)中的版本号

.NET SDK-Style 项目&#xff08;Core、Standard、.NET5&#xff09;中的版本号独立观察员 2020 年 12 月 24 日之前 .NET Framework 时&#xff0c;项目版本号等信息是存储在 AssemblyInfo.cs 文件中&#xff0c;通过程序集特性进行设置&#xff1a;.NET Core 之后&#xff0c…

ajax php 动态,jQuery+PHP+Ajax实现动态数字统计展示功能

jQueryPHPAjax实现的一款动态数字统计展示实例&#xff0c;本例是在页面上动态展示了当前在线用户数&#xff0c;当然了&#xff0c;你可以应用到其他更多场景中。首先我们在#number放置要统计的数字&#xff1a;当前在线&#xff1a;然后我们要定义一个动画过程&#xff0c;使…

[Stardust]星尘分布式全链路监控

随着业务的发展&#xff0c;微服务系统会变得越来越大&#xff0c;各个服务之间的调用关系也会日趋复杂。一个WebApi请求&#xff0c;后方可能经历多个微服务以及数据库和MQ操作&#xff0c;在这个调用过程中&#xff0c;可能因为某一个服务节点出现延迟或者失败&#xff0c;而…

php去掉查询返回的字段序列,php数组函数序列之array_slice() - 在数组中根据条件取出一段值,并返回...

array_slice()定义和用法array_slice() 函数在数组中根据条件取出一段值&#xff0c;并返回。注释&#xff1a;如果数组有字符串键&#xff0c;所返回的数组将保留键名。(参见例子 4)语法array_slice(array,offset,length,preserve)参数 描述array 必需。规定输入的数组。offse…

如何在 C# 中使用 const,readonly,static

平时在开发时经常会用到 const,readonly,static 关键字&#xff0c;可以肯定这些关键词是完全不同的概念&#xff0c;但有时候他们在用法上很相似以至于在场景中不知道选择哪一个&#xff0c;这篇文章我们就来讨论 C# 中的 const&#xff0c;static 和 readonly 关键词&#xf…

php html asp .net iis tomcat,iis+apache+tomcat 整合共享80口 支持ASP .NET JSP PHP全能WEB服务...

文件目录说明&#xff1a;[apache-tomcat-6.0.37-windows-x86] tomcat 安装版与解压版文件[httpd-2.2] apache安装文件[java] jdk与jre安装文件[mysql] mysql与mysql-tool安装文件[php-5.2.14-Win32] php解压文件[tomcat-connectors-1.2.14-isapi-iis-6.0] isapi模块安装文件[t…

2020,你收获了什么?又失去了什么?

这是头哥侃码的第228篇原创今天是圣诞节&#xff0c;既魔幻又真实的2020年&#xff0c;还有6天就要结束了。每年的年末&#xff0c;我都习惯在下班途中&#xff0c;把头靠在地铁车厢两侧的扶手上&#xff0c;闭上眼睛开始思考自己过去一年的收获与得失&#xff0c;并把这些思绪…

2019龙少php泛站群,龙少php泛站群|PHP版站群 全自动泛解析站群程序 赠送教程

首先将准备建站的域名设置泛解析如baidu.com泛解析为 添加A记录为 *.baidu.com然后在iis里添加空头主机不能设置 汉字目录keywords.txt 关键字&#xff0c;一行一个domain.txt 域名&#xff0c;一行一个&#xff0c;*代表随机muban.html 模板文件&#xff0c;可以修改缓存在dat…

轻量级消息队列RedisQueue

消息队列&#xff08;Message Queue&#xff09;是分布式系统必不可少的中间件&#xff0c;大部分消息队列产品&#xff08;如RocketMQ/RabbitMQ/Kafka等&#xff09;要求团队有比较强的技术实力&#xff0c;不适用于中小团队&#xff0c;并且对.NET技术的支持力度不够。而Redi…

php下载文件代码 数据库,PHP备份数据库生成SQL文件并下载的函数代码

. 代码如下:/****** 备份数据库结构 ******//*函数名称&#xff1a;table2sql()函数功能&#xff1a;把表的结构转换成为SQL函数参数&#xff1a;$table: 要进行提取的表名返 回 值&#xff1a;返回提取后的结果&#xff0c;SQL集合函数作者&#xff1a;heiyeluren*/function t…

生态和能力是国内自研操作系统发展的关键

“缺芯少魂”一直是我国信息产业短板&#xff0c;如果无法实现国产化替代&#xff0c;信息安全和产业安全就犹如沙滩上盖房子&#xff0c;上层再坚固&#xff0c;地基不稳&#xff0c;一遇到风吹草动就有可能全部垮掉。近年来&#xff0c;国内自研操作系统厂商动作频频&#xf…

php curl 使用方法,php curl使用方法与步骤

本书特别新手的一点&#xff0c;是将重要操作的核心思路都为大家提供了。在数据库、图片、文件上传我们都把真正的步骤。以及步骤如何组何成为我们的代码和实现过程都做过讲解。我们将curl的步骤分为以下7步&#xff1a;1.初使化curl资源2.参数设置请求的协议地址3.参数设置是否…