从壹开始前后端分离 [.netCore 填坑 ] 三十四║Swagger:API多版本控制,带来的思考

前言

大家周二好呀,.net core + Vue 这一系列基本就到这里差不多了,今天我又把整个系列的文章下边的全部评论看了一下(我是不是很负责哈哈),提到的问题基本都解决了,还有一些问题,已经在QQ群里讨论过了,今天再写一篇,然后给这个系列画一个暂时的句号吧,这些天也考虑写点儿啥,希望看到的小伙伴给点儿意见哟,其实我也是能力有限,不敢保证精通,不过只要想学,基本都能学到点儿东西的,至少至少能给大家在繁忙或者无聊的开发生涯中,多一点儿学习的动力吧,至少群里边的小伙伴是这样(马上破百了,快来加入我们吧),目前写的都是浅显的,打算下一步向架构师微服务方向简单拓展下,这两天简单看了一下,真是云里来雾里去,不是很通俗,还得自己啃,一起加油吧!!!

1、什么是版本控制

这个词语大家已经不会陌生,平时开发的时候,一定会用到过 Git 、SVN 或者 VSS (这三个我都用过,Git 应该是最好的),这个就是源代码的版本控制。

来句官方定义:版本控制是指对软件开发过程中各种程序代码、配置文件及说明文档等文件变更的管理,是软件配置管理的核心思想之一。

那今天我们说的,就是 api接口的版本控制,这个大家一定也都接触到了,在我们使用的 swagger 中是这样的:

image

2、api版本控制的好处

简单来说,接口是APP的重要组成部分,数据是APP的核心,接口是连接APP和数据的纽带(这里的 APP 是广义上的接口调用者)。

一般情况下,我们项目中会有大量的接口,再加上版本的变化,接口的升级,一个接口,可能会有很多个稍有差异的接口,这个时候接口如果维护的不好,错一个就是一大片,那我们对 api 进行版本控制的好处有:

(1)有助于保护原有系统,不受影响,并及时修复问题
(2)可以实现用户的私人定制,(我之前接触过付费接口,可以这个意思)。
(3)快速迭代。

之前我在开发的时候,倒是没有考虑过这个问题,都是想当然的以为写代码只有一个版本,亦或者根本就没有版本概念,昨天晚上在看有一个小伙伴问到了 swagger 中,如何进行版本控制( 然后我想了想,在平时的开发中,我开发的项目中还没有遇到过版本控制,都是 web 项目+控制台项目,有问题就直接修改,有 bug 直接覆盖那种,从来没有考虑过版本,但是既然咱们这个系列是基于 api 接口的,版本应该是要有的,而且相信以后如果开发 api 项目的时候,也会遇到这个问题。我就研究了下 swagger 的源码,结合着网上的资料看了看,简单的配置了下,是这样的:

image

3、常见的版本控制有哪些?

通过上边的配置,我自认为很好的解决了这个问题,但是当我深入学习的时候,发现并不是,比如如何很好的调用不同版本的接口?,前端又如何对写好的接口地址进行快速修改?等等多个问题引起我的思考,通过搜索资料,我总结了以下,常见的版本控制有以下几个方案:

0、直接修改方法名,比如:/api/blog_v1,/api/blog_v2,/api/blog_v3... 虽然有时候也用,不过我直接 pass

1、通过路由控制,比如豆瓣:https://api.douban.com/v2/movie/in_theaters //本文重点说明,个人推荐,其他的大家可以参考博友文章

2、通过参数选择,比如:http://localhost:58427/api/Values?api-version=2.0

3、通过http请求的 Headers 来控制,接口地址不变,下边会说到

4、利用 content type 来控制

老张:本文只是一个说明版本,并没有把所有的方案都 code 出来,重点说了下路由控制,剩下的只是引导大家去思考这个问题,然后继续学习,毕竟会一两个方法就行了,平时开发中,使用的并不是很频繁,有好的想法欢迎下边留言,或者来群里和我们的小伙伴热情互动吧!

一、在 swagger 中通过路由实现版本控制

1、注册多个版本api

1、在 Blog.Core 项目下新建 SwaggerHelper 文件夹,然后添加 CustomApiVersion.cs 用来控制版本

image

2、在自定义API版本类中,添加枚举版本号

/// <summary>
    /// 自定义版本 /// </summary>
    public class CustomApiVersion
    { /// <summary>
        /// Api接口版本 自定义 /// </summary>
        public enum ApiVersions
        { /// <summary>
            /// v1 版本 /// </summary>
            v1 = 1, /// <summary>
            /// v2 版本 /// </summary>
            v2 = 2,
        }
    }

3、在项目启动类 Startup.cs 中,配置服务,遍历版本展示

在 ConfigureServices 方法内,修改 services.AddSwaggerGen 中的 c.SwaggerDoc 文档如下:

//遍历出全部的版本,做文档信息展示
typeof(ApiVersions).GetEnumNames().ToList().ForEach(version => {
    c.SwaggerDoc(version, new Info
    { // {ApiName} 定义成全局变量,方便修改
        Version = version,
        Title = $"{ApiName} 接口文档",
        Description = $"{ApiName} HTTP API " + version,
        TermsOfService = "None",
        Contact = new Contact { Name = "Blog.Core", Email = "Blog.Core@xxx.com", Url = "https://www.jianshu.com/u/94102b59cc2a" }
    });
});

4、修改 SwagerUI 调用配置

在 Configure 方法内,修改 app.UseSwaggerUI 如下:

app.UseSwaggerUI(c => { //之前是写死的 //c.SwaggerEndpoint("/swagger/v1/swagger.json", "ApiHelp V1"); //c.RoutePrefix = "";//路径配置,设置为空,表示直接在根域名(localhost:8001)访问该文件 //根据版本名称倒序 遍历展示
    typeof(ApiVersions).GetEnumNames().OrderByDescending(e => e).ToList().ForEach(version => {
        c.SwaggerEndpoint($"/swagger/{version}/swagger.json", $"{ApiName} {version}");
    });
});

5、查看效果

image

现在已经实现了,在 swagger 中,进行多版本的展示,那要如何进行控制呢,请往下看。

2、对接口进行版本配置

1、刚刚我们已经创建好了多版本的接口文档,那现在就需要配置接口api了

在 BlogController.cs 中新建一个 V2_Blogtest() 方法:

   /// <summary>
   /// 获取博客测试信息 v2版本 /// </summary>
   /// <returns></returns>
 [HttpGet] //MVC自带特性 对 api 进行组管理
   [ApiExplorerSettings(GroupName = "v2")] //路径 如果以 / 开头,表示绝对路径,反之相对 controller 的想u地路径
   [Route("/api/v2/blog/Blogtest")] public async Task<object> V2_Blogtest()
   { return Ok(new { status = 220, data = "我是第二版的博客信息" });

   }

这里用到了 ApiExplorerSettings 特性,在mvc开发中,自带的一个组管理。

为什么要配置路径呢?是因为多版本的情况下,可能会出现重名函数,这里没有体现出来,因为使用的是 :V2_Blogtest ,下边的文章中会说到,如果一定要重名,需要怎么做。

2、这个时候查看效果,发现已经实现了我们文件开头的效果

image

这个时候效果已经实现了,但是这么写显然不是很方便,首先,我们的组名 GroupName 是写死的 ”v2“,不利用拓展,然后呢,还需要再一次配置路由 Route,有小伙伴就发现了,既然这两个都是特性,有没有办法重写一个特性,把这两个合并呢,欸?!就是这样,请往下看。

3、自定义路由特性,实现路由+版本 双控制

1、在根目录的 SwaggerHelper 文件夹下,新建一个 CustomRouteAttribute.cs

/// <summary>
    /// 自定义路由 /api/{version}/[controler]/[action] /// </summary>
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)] public class CustomRouteAttribute : RouteAttribute, IApiDescriptionGroupNameProvider
    { /// <summary>
        /// 分组名称,是来实现接口 IApiDescriptionGroupNameProvider /// </summary>
        public string GroupName { get; set; } /// <summary>
        /// 自定义路由构造函数,继承基类路由 /// </summary>
        /// <param name="actionName"></param>
        public CustomRouteAttribute(string actionName = "[action]") : base("/api/{version}/[controller]/" + actionName)
        {
        } /// <summary>
        /// 自定义版本+路由构造函数,继承基类路由 /// </summary>
        /// <param name="actionName"></param>
        /// <param name="version"></param>
        public CustomRouteAttribute(ApiVersions version, string actionName = "[action]") : base($"/api/{version.ToString()}/[controller]/{actionName}")
        {
            GroupName = version.ToString();
        }
    }

2、对 api 接口进行设置

 /// <summary>
 /// 获取博客测试信息 v2版本 /// </summary>
 /// <returns></returns>
 [HttpGet] ////MVC自带特性 对 api 进行组管理
 //[ApiExplorerSettings(GroupName = "v2")]
 ////路径 如果以 / 开头,表示绝对路径,反之相对 controller 的想u地路径
 //[Route("/api/v2/blog/Blogtest")] //和上边的版本控制以及路由地址都是一样的
 [CustomRoute(ApiVersions.v2, "Blogtest")] public async Task<object> V2_Blogtest()
 { return Ok(new { status = 220, data = "我是第二版的博客信息" });

 }

浏览效果都是一样的,这里就不展示了,从这里看出来,还是很方便的。

说到这里,基于 swagger 的api接口版本控制已经说完了,采用的方法是路由控制,我个人感觉还是挺好的,当然文章的开头也说到了,还是有其他的方法,这里就简单的其中一个,个人不是很推荐,但是大家可以看看。

二、同名接口的版本控制

在上边咱们说到了,如果两个版本的方法名一定要一直咋办呢,重载大家肯定都知道,但是同一个 controller 接口方法肯定无论参数还是名称全部都一样,就连返回类型也一样,所以不能重载,那我们应该怎么办呢?,请往下看。

1、 在 controller 文件夹下,新建两个文件夹, v1、v2

2、然后添加相同的接口控制器 ApbController.cs,自定义即可

image

3、在两个控制器中,添加相同的代码

image

这样就能实现同名方法的版本控制了。

三、其他不适用于 swagger 的接口版本控制方法

这些方法我本打算写下来,发现不能通过 swagger 展示,会报错,只能通过 postman 测试,所以对我来说不是很完美,这里把博友的文章贴出来,大家可以自己看一下。

ASP.Net Core WebApi几种版本控制对比

ASP.NET Core API 版本控制

Your API versioning is wrong, which is why I decided to do it 3 different wrong ways

四、Github

https://github.com/anjoy8/Blog.Core

https://gitee.com/laozhangIsPhi/Blog.Core

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,542评论 6 504
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,822评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,912评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,449评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,500评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,370评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,193评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,074评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,505评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,722评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,841评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,569评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,168评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,783评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,918评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,962评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,781评论 2 354

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 172,114评论 25 707
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,656评论 18 139
  • 用两张图告诉你,为什么你的 App 会卡顿? - Android - 掘金 Cover 有什么料? 从这篇文章中你...
    hw1212阅读 12,723评论 2 59
  • (三)我知道你不是一个冷酷的人 “怎么不早点儿叫我?”高文书略带责备的声音让武凌恢复了神智。 “你先帮我扶着点儿琪...
    趁早退场阅读 435评论 0 2
  • 1、习惯 “人应该支配习惯,而决不能让习惯支配人,一个人不能去掉他的坏习惯,那简直一文不值。——奥斯特洛夫斯基 习...
    邓成渝阅读 173评论 2 0