您当前的位置:首页 > 电脑百科 > 程序开发 > 语言 > .NET

.Net WebApi 实现 接口版本控制并打通Swagger支持

时间:2022-07-14 16:35:27  来源:  作者:IT技术资源爱好者

我们在开发 webapi 项目时如果遇到 api 接口需要同时支持多个版本的时候,比如接口修改了入参之后但是又希望支持老版本的前端(这里的前端可能是网页,可能是App,小程序 等等)进行调用,这种情况常见于 app,毕竟网页前端我们可以主动控制发布,只要统一发布后所有人的浏览器下一次访问网页时都会重新加载到最新版的代码,但是像 app 则无法保证用户一定会第一时间升级更新最新版的app,所以往往需要 api接口能够同时保持多个版本的逻辑,同支持新老版本的调用端app进行调用。

针对上面的描述举一个例子:

比如一个创建用户的接口,api/user/createuser

如果我们这个时候对该接口的入参和返回参数修改之后,但是又希望原本的 api/user/createuser 接口逻辑也可以正常运行,常见的做法有以下几种:

  1. 修改接口名称,将新的创建用户接口地址定义为 api/user/newcreateuser
  2. url传入版本标记,将新的创建用户接口地址定义为 api/user/createuser?api-version=2
  3. 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,如下图的结构

.Net WebApi 实现 接口版本控制并打通Swagger支持

 

然后只要在对应文件夹下的控制器头部加入版本标记

[ApiVersion("1")] [ApiVersion("2")] [ApiVersion("......")]

如下图的两个控制器

.Net WebApi 实现 接口版本控制并打通Swagger支持

 

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

.Net WebApi 实现 接口版本控制并打通Swagger支持

 

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

.Net WebApi 实现 接口版本控制并打通Swagger支持

 

​点击单个接口的 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 就会出现下图的样式

.Net WebApi 实现 接口版本控制并打通Swagger支持

 

​ 通过 SwaggerDoc 就可以很明确的看出 v1 版本的 api 已经被淘汰了。



Tags:.Net   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,不构成投资建议。投资者据此操作,风险自担。如有任何标注错误或版权侵犯请与我们联系,我们将及时更正、删除。
▌相关推荐
.Net开发中十种常见的内存错误以及相应的解决方案
在.Net开发中,为内存管理方面提供了许多便利,但仍然存在一些常见的错误和陷阱。这些错误可能导致内存泄漏、性能下降、异常抛出等问题,严重影响应用程序的稳定性和性能。在软件...【详细内容】
2024-03-26  Search: .Net  点击:(18)  评论:(0)  加入收藏
.NET配置文件大揭秘:轻松读取JSON、XML、INI和环境变量
概述:.NET中的IConfiguration接口提供了一种多源读取配置信息的灵活机制,包括JSON、XML、INI文件和环境变量。通过示例,清晰演示了从这些不同源中读取配置的方法,使配置获取变得...【详细内容】
2023-12-28  Search: .Net  点击:(94)  评论:(0)  加入收藏
.Net开发都应当掌握的泛型基础知识集合
在C#编程语言中,泛型是一项非常强大和重要的功能。它允许我们编写更加灵活和通用的代码,同时提高代码的可重用性和性能。本文将介绍C#泛型的一些关键知识点,帮助读者理解和应用...【详细内容】
2023-12-25  Search: .Net  点击:(128)  评论:(0)  加入收藏
作为 .NET 开发人员,我为什么开始使用 Python?
作者 | Alex Maher编译 | 小欧作为一名 .NET 开发人员,很长一段时间以来,我一直关注 C# 和 .NET 的出色工具和功能。但我最近开始使用 Python,感觉非常棒。这里申明一点,这篇文...【详细内容】
2023-12-25  Search: .Net  点击:(147)  评论:(0)  加入收藏
.NET领域性能最好的对象映射框架Mapster使用方法
Mapster是一个开源的.NET对象映射库,它提供了一种简单而强大的方式来处理对象之间的映射。在本文中,我将详细介绍如何在.NET中使用Mapster,并提供一些实例和源代码。和其它框架...【详细内容】
2023-12-22  Search: .Net  点击:(86)  评论:(0)  加入收藏
.NET Core 3.1 升级到 .NET 8,看看都有哪些变化
.NET Core 3.1 已经用了很长一段时间,其实在 2022 年的年底微软已经不提供支持了,后面的一个 LTS 版本 .NET 6 也会在 2024 年 11 月终止支持,所以直接升级到 .NET 8 是最好的...【详细内容】
2023-12-08  Search: .Net  点击:(196)  评论:(0)  加入收藏
.NET Core的中间件来对Web API进行流量限制实现方法
在.NET Core中,我们可以使用ASP.NET Core的中间件来对Web API进行流量限制。ASP.NET Core提供了一个名为RateLimit的开源库,可以方便地实现流量限制功能。下面将详细介绍如何...【详细内容】
2023-12-06  Search: .Net  点击:(179)  评论:(0)  加入收藏
微软官方出品微服务架构:十个.Net开源项目
1、一个高性能类型安全的.NET枚举实用开源库Enums.NET是一个.NET枚举实用程序库,专注于为枚举提供丰富的操作方法。它支持.NET Framework和.Net Core。它主要优点表现在类型...【详细内容】
2023-12-06  Search: .Net  点击:(137)  评论:(0)  加入收藏
.NET开源的处理分布式事务的解决方案
前言在分布式系统中,由于各个系统服务之间的独立性和网络通信的不确定性,要确保跨系统的事务操作的最终一致性是一项重大的挑战。今天给大家推荐一个.NET开源的处理分布式事务...【详细内容】
2023-11-30  Search: .Net  点击:(173)  评论:(0)  加入收藏
深入 .NET 异步编程:Task 与 ValueTask 的区别与选择
在 .NET 中,Task 和 ValueTask 都是用于表示异步操作的类型,但它们有一些重要的区别。TaskTask 是最常见的表示异步操作的类型。它通常用于表示耗时的、异步的操作,比如从文件...【详细内容】
2023-11-26  Search: .Net  点击:(191)  评论:(0)  加入收藏
▌简易百科推荐
.Net开发中十种常见的内存错误以及相应的解决方案
在.Net开发中,为内存管理方面提供了许多便利,但仍然存在一些常见的错误和陷阱。这些错误可能导致内存泄漏、性能下降、异常抛出等问题,严重影响应用程序的稳定性和性能。在软件...【详细内容】
2024-03-26  小乖兽技术  今日头条  Tags:.Net   点击:(18)  评论:(0)  加入收藏
.NET配置文件大揭秘:轻松读取JSON、XML、INI和环境变量
概述:.NET中的IConfiguration接口提供了一种多源读取配置信息的灵活机制,包括JSON、XML、INI文件和环境变量。通过示例,清晰演示了从这些不同源中读取配置的方法,使配置获取变得...【详细内容】
2023-12-28  架构师老卢  今日头条  Tags:.NET   点击:(94)  评论:(0)  加入收藏
.Net开发都应当掌握的泛型基础知识集合
在C#编程语言中,泛型是一项非常强大和重要的功能。它允许我们编写更加灵活和通用的代码,同时提高代码的可重用性和性能。本文将介绍C#泛型的一些关键知识点,帮助读者理解和应用...【详细内容】
2023-12-25  小乖兽技术  今日头条  Tags:.Net   点击:(128)  评论:(0)  加入收藏
作为 .NET 开发人员,我为什么开始使用 Python?
作者 | Alex Maher编译 | 小欧作为一名 .NET 开发人员,很长一段时间以来,我一直关注 C# 和 .NET 的出色工具和功能。但我最近开始使用 Python,感觉非常棒。这里申明一点,这篇文...【详细内容】
2023-12-25    51CTO  Tags:.NET   点击:(147)  评论:(0)  加入收藏
.NET Core 3.1 升级到 .NET 8,看看都有哪些变化
.NET Core 3.1 已经用了很长一段时间,其实在 2022 年的年底微软已经不提供支持了,后面的一个 LTS 版本 .NET 6 也会在 2024 年 11 月终止支持,所以直接升级到 .NET 8 是最好的...【详细内容】
2023-12-08  不止dotNET  微信公众号  Tags:.NET   点击:(196)  评论:(0)  加入收藏
.NET Core的中间件来对Web API进行流量限制实现方法
在.NET Core中,我们可以使用ASP.NET Core的中间件来对Web API进行流量限制。ASP.NET Core提供了一个名为RateLimit的开源库,可以方便地实现流量限制功能。下面将详细介绍如何...【详细内容】
2023-12-06  架构师老卢  今日头条  Tags:.NET   点击:(179)  评论:(0)  加入收藏
微软官方出品微服务架构:十个.Net开源项目
1、一个高性能类型安全的.NET枚举实用开源库Enums.NET是一个.NET枚举实用程序库,专注于为枚举提供丰富的操作方法。它支持.NET Framework和.Net Core。它主要优点表现在类型...【详细内容】
2023-12-06  编程乐趣  今日头条  Tags:.Net   点击:(137)  评论:(0)  加入收藏
.NET开源的处理分布式事务的解决方案
前言在分布式系统中,由于各个系统服务之间的独立性和网络通信的不确定性,要确保跨系统的事务操作的最终一致性是一项重大的挑战。今天给大家推荐一个.NET开源的处理分布式事务...【详细内容】
2023-11-30  追逐时光者  微信公众号  Tags:.NET   点击:(173)  评论:(0)  加入收藏
深入 .NET 异步编程:Task 与 ValueTask 的区别与选择
在 .NET 中,Task 和 ValueTask 都是用于表示异步操作的类型,但它们有一些重要的区别。TaskTask 是最常见的表示异步操作的类型。它通常用于表示耗时的、异步的操作,比如从文件...【详细内容】
2023-11-26  架构师老卢  微信公众号  Tags: .NET   点击:(191)  评论:(0)  加入收藏
.NET字符串存储:解析常量与动态字符串,深入了解内存机制
在 .NET 中,字符串是不可变的,这意味着一旦创建,字符串的内容就不能被修改。字符串在内存中以不同的方式存储,具体取决于它是常量字符串还是动态创建的字符串。常量字符串常量字...【详细内容】
2023-11-25  架构师老卢  微信公众号  Tags:.NET   点击:(206)  评论:(0)  加入收藏
站内最新
站内热门
站内头条