從壹開(kāi)始前后端分離 [.netCore 填坑 ] 三十四║Swagger:API多版本控制,帶來(lái)的思考

前言

大家周二好呀,.net core + Vue 這一系列基本就到這里差不多了,今天我又把整個(gè)系列的文章下邊的全部評(píng)論看了一下(我是不是很負(fù)責(zé)哈哈),提到的問(wèn)題基本都解決了,還有一些問(wèn)題,已經(jīng)在QQ群里討論過(guò)了,今天再寫(xiě)一篇,然后給這個(gè)系列畫(huà)一個(gè)暫時(shí)的句號(hào)吧,這些天也考慮寫(xiě)點(diǎn)兒啥,希望看到的小伙伴給點(diǎn)兒意見(jiàn)喲,其實(shí)我也是能力有限,不敢保證精通,不過(guò)只要想學(xué),基本都能學(xué)到點(diǎn)兒東西的,至少至少能給大家在繁忙或者無(wú)聊的開(kāi)發(fā)生涯中,多一點(diǎn)兒學(xué)習(xí)的動(dòng)力吧,至少群里邊的小伙伴是這樣(馬上破百了,快來(lái)加入我們吧),目前寫(xiě)的都是淺顯的,打算下一步向架構(gòu)師微服務(wù)方向簡(jiǎn)單拓展下,這兩天簡(jiǎn)單看了一下,真是云里來(lái)霧里去,不是很通俗,還得自己啃,一起加油吧!??!

1、什么是版本控制

這個(gè)詞語(yǔ)大家已經(jīng)不會(huì)陌生,平時(shí)開(kāi)發(fā)的時(shí)候,一定會(huì)用到過(guò) Git 、SVN 或者 VSS (這三個(gè)我都用過(guò),Git 應(yīng)該是最好的),這個(gè)就是源代碼的版本控制。

來(lái)句官方定義:版本控制是指對(duì)軟件開(kāi)發(fā)過(guò)程中各種程序代碼、配置文件及說(shuō)明文檔等文件變更的管理,是軟件配置管理的核心思想之一。

那今天我們說(shuō)的,就是 api接口的版本控制,這個(gè)大家一定也都接觸到了,在我們使用的 swagger 中是這樣的:

image

2、api版本控制的好處

簡(jiǎn)單來(lái)說(shuō),接口是APP的重要組成部分,數(shù)據(jù)是APP的核心,接口是連接APP和數(shù)據(jù)的紐帶(這里的 APP 是廣義上的接口調(diào)用者)。

一般情況下,我們項(xiàng)目中會(huì)有大量的接口,再加上版本的變化,接口的升級(jí),一個(gè)接口,可能會(huì)有很多個(gè)稍有差異的接口,這個(gè)時(shí)候接口如果維護(hù)的不好,錯(cuò)一個(gè)就是一大片,那我們對(duì) api 進(jìn)行版本控制的好處有:

(1)有助于保護(hù)原有系統(tǒng),不受影響,并及時(shí)修復(fù)問(wèn)題
(2)可以實(shí)現(xiàn)用戶(hù)的私人定制,(我之前接觸過(guò)付費(fèi)接口,可以這個(gè)意思)。
(3)快速迭代。

之前我在開(kāi)發(fā)的時(shí)候,倒是沒(méi)有考慮過(guò)這個(gè)問(wèn)題,都是想當(dāng)然的以為寫(xiě)代碼只有一個(gè)版本,亦或者根本就沒(méi)有版本概念,昨天晚上在看有一個(gè)小伙伴問(wèn)到了 swagger 中,如何進(jìn)行版本控制( 然后我想了想,在平時(shí)的開(kāi)發(fā)中,我開(kāi)發(fā)的項(xiàng)目中還沒(méi)有遇到過(guò)版本控制,都是 web 項(xiàng)目+控制臺(tái)項(xiàng)目,有問(wèn)題就直接修改,有 bug 直接覆蓋那種,從來(lái)沒(méi)有考慮過(guò)版本,但是既然咱們這個(gè)系列是基于 api 接口的,版本應(yīng)該是要有的,而且相信以后如果開(kāi)發(fā) api 項(xiàng)目的時(shí)候,也會(huì)遇到這個(gè)問(wèn)題。我就研究了下 swagger 的源碼,結(jié)合著網(wǎng)上的資料看了看,簡(jiǎn)單的配置了下,是這樣的:

image

3、常見(jiàn)的版本控制有哪些?

通過(guò)上邊的配置,我自認(rèn)為很好的解決了這個(gè)問(wèn)題,但是當(dāng)我深入學(xué)習(xí)的時(shí)候,發(fā)現(xiàn)并不是,比如如何很好的調(diào)用不同版本的接口?,前端又如何對(duì)寫(xiě)好的接口地址進(jìn)行快速修改?等等多個(gè)問(wèn)題引起我的思考,通過(guò)搜索資料,我總結(jié)了以下,常見(jiàn)的版本控制有以下幾個(gè)方案:

0、直接修改方法名,比如:/api/blog_v1,/api/blog_v2,/api/blog_v3... 雖然有時(shí)候也用,不過(guò)我直接 pass

1、通過(guò)路由控制,比如豆瓣:https://api.douban.com/v2/movie/in_theaters //本文重點(diǎn)說(shuō)明,個(gè)人推薦,其他的大家可以參考博友文章

2、通過(guò)參數(shù)選擇,比如:http://localhost:58427/api/Values?api-version=2.0

3、通過(guò)http請(qǐng)求的 Headers 來(lái)控制,接口地址不變,下邊會(huì)說(shuō)到

4、利用 content type 來(lái)控制

老張:本文只是一個(gè)說(shuō)明版本,并沒(méi)有把所有的方案都 code 出來(lái),重點(diǎn)說(shuō)了下路由控制,剩下的只是引導(dǎo)大家去思考這個(gè)問(wèn)題,然后繼續(xù)學(xué)習(xí),畢竟會(huì)一兩個(gè)方法就行了,平時(shí)開(kāi)發(fā)中,使用的并不是很頻繁,有好的想法歡迎下邊留言,或者來(lái)群里和我們的小伙伴熱情互動(dòng)吧!

一、在 swagger 中通過(guò)路由實(shí)現(xiàn)版本控制

1、注冊(cè)多個(gè)版本api

1、在 Blog.Core 項(xiàng)目下新建 SwaggerHelper 文件夾,然后添加 CustomApiVersion.cs 用來(lái)控制版本

image

2、在自定義API版本類(lèi)中,添加枚舉版本號(hào)

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

3、在項(xiàng)目啟動(dòng)類(lèi) Startup.cs 中,配置服務(wù),遍歷版本展示

在 ConfigureServices 方法內(nèi),修改 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 = "http://www.itdecent.cn/u/94102b59cc2a" }
    });
});

4、修改 SwagerUI 調(diào)用配置

在 Configure 方法內(nèi),修改 app.UseSwaggerUI 如下:

app.UseSwaggerUI(c => { //之前是寫(xiě)死的 //c.SwaggerEndpoint("/swagger/v1/swagger.json", "ApiHelp V1"); //c.RoutePrefix = "";//路徑配置,設(shè)置為空,表示直接在根域名(localhost:8001)訪(fǎng)問(wèn)該文件 //根據(jù)版本名稱(chēng)倒序 遍歷展示
    typeof(ApiVersions).GetEnumNames().OrderByDescending(e => e).ToList().ForEach(version => {
        c.SwaggerEndpoint($"/swagger/{version}/swagger.json", $"{ApiName} {version}");
    });
});

5、查看效果

image

現(xiàn)在已經(jīng)實(shí)現(xiàn)了,在 swagger 中,進(jìn)行多版本的展示,那要如何進(jìn)行控制呢,請(qǐng)往下看。

2、對(duì)接口進(jìn)行版本配置

1、剛剛我們已經(jīng)創(chuàng)建好了多版本的接口文檔,那現(xiàn)在就需要配置接口api了

在 BlogController.cs 中新建一個(gè) V2_Blogtest() 方法:

   /// <summary>
   /// 獲取博客測(cè)試信息 v2版本 /// </summary>
   /// <returns></returns>
 [HttpGet] //MVC自帶特性 對(duì) api 進(jìn)行組管理
   [ApiExplorerSettings(GroupName = "v2")] //路徑 如果以 / 開(kāi)頭,表示絕對(duì)路徑,反之相對(duì) controller 的想u地路徑
   [Route("/api/v2/blog/Blogtest")] public async Task<object> V2_Blogtest()
   { return Ok(new { status = 220, data = "我是第二版的博客信息" });

   }

這里用到了 ApiExplorerSettings 特性,在mvc開(kāi)發(fā)中,自帶的一個(gè)組管理。

為什么要配置路徑呢?是因?yàn)槎喟姹镜那闆r下,可能會(huì)出現(xiàn)重名函數(shù),這里沒(méi)有體現(xiàn)出來(lái),因?yàn)槭褂玫氖?:V2_Blogtest ,下邊的文章中會(huì)說(shuō)到,如果一定要重名,需要怎么做。

2、這個(gè)時(shí)候查看效果,發(fā)現(xiàn)已經(jīng)實(shí)現(xiàn)了我們文件開(kāi)頭的效果

image

這個(gè)時(shí)候效果已經(jīng)實(shí)現(xiàn)了,但是這么寫(xiě)顯然不是很方便,首先,我們的組名 GroupName 是寫(xiě)死的 ”v2“,不利用拓展,然后呢,還需要再一次配置路由 Route,有小伙伴就發(fā)現(xiàn)了,既然這兩個(gè)都是特性,有沒(méi)有辦法重寫(xiě)一個(gè)特性,把這兩個(gè)合并呢,欸?!就是這樣,請(qǐng)往下看。

3、自定義路由特性,實(shí)現(xiàn)路由+版本 雙控制

1、在根目錄的 SwaggerHelper 文件夾下,新建一個(gè) CustomRouteAttribute.cs

/// <summary>
    /// 自定義路由 /api/{version}/[controler]/[action] /// </summary>
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)] public class CustomRouteAttribute : RouteAttribute, IApiDescriptionGroupNameProvider
    { /// <summary>
        /// 分組名稱(chēng),是來(lái)實(shí)現(xiàn)接口 IApiDescriptionGroupNameProvider /// </summary>
        public string GroupName { get; set; } /// <summary>
        /// 自定義路由構(gòu)造函數(shù),繼承基類(lèi)路由 /// </summary>
        /// <param name="actionName"></param>
        public CustomRouteAttribute(string actionName = "[action]") : base("/api/{version}/[controller]/" + actionName)
        {
        } /// <summary>
        /// 自定義版本+路由構(gòu)造函數(shù),繼承基類(lèi)路由 /// </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、對(duì) api 接口進(jìn)行設(shè)置

 /// <summary>
 /// 獲取博客測(cè)試信息 v2版本 /// </summary>
 /// <returns></returns>
 [HttpGet] ////MVC自帶特性 對(duì) api 進(jìn)行組管理
 //[ApiExplorerSettings(GroupName = "v2")]
 ////路徑 如果以 / 開(kāi)頭,表示絕對(duì)路徑,反之相對(duì) controller 的想u地路徑
 //[Route("/api/v2/blog/Blogtest")] //和上邊的版本控制以及路由地址都是一樣的
 [CustomRoute(ApiVersions.v2, "Blogtest")] public async Task<object> V2_Blogtest()
 { return Ok(new { status = 220, data = "我是第二版的博客信息" });

 }

瀏覽效果都是一樣的,這里就不展示了,從這里看出來(lái),還是很方便的。

說(shuō)到這里,基于 swagger 的api接口版本控制已經(jīng)說(shuō)完了,采用的方法是路由控制,我個(gè)人感覺(jué)還是挺好的,當(dāng)然文章的開(kāi)頭也說(shuō)到了,還是有其他的方法,這里就簡(jiǎn)單的其中一個(gè),個(gè)人不是很推薦,但是大家可以看看。

二、同名接口的版本控制

在上邊咱們說(shuō)到了,如果兩個(gè)版本的方法名一定要一直咋辦呢,重載大家肯定都知道,但是同一個(gè) controller 接口方法肯定無(wú)論參數(shù)還是名稱(chēng)全部都一樣,就連返回類(lèi)型也一樣,所以不能重載,那我們應(yīng)該怎么辦呢?,請(qǐng)往下看。

1、 在 controller 文件夾下,新建兩個(gè)文件夾, v1、v2

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

image

3、在兩個(gè)控制器中,添加相同的代碼

image

這樣就能實(shí)現(xiàn)同名方法的版本控制了。

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

這些方法我本打算寫(xiě)下來(lái),發(fā)現(xiàn)不能通過(guò) swagger 展示,會(huì)報(bào)錯(cuò),只能通過(guò) postman 測(cè)試,所以對(duì)我來(lái)說(shuō)不是很完美,這里把博友的文章貼出來(lái),大家可以自己看一下。

ASP.Net Core WebApi幾種版本控制對(duì)比

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

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀(guān)點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 178,781評(píng)論 25 709
  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,506評(píng)論 19 139
  • 用兩張圖告訴你,為什么你的 App 會(huì)卡頓? - Android - 掘金 Cover 有什么料? 從這篇文章中你...
    hw1212閱讀 13,914評(píng)論 2 59
  • (三)我知道你不是一個(gè)冷酷的人 “怎么不早點(diǎn)兒叫我?”高文書(shū)略帶責(zé)備的聲音讓武凌恢復(fù)了神智。 “你先幫我扶著點(diǎn)兒琪...
    趁早退場(chǎng)閱讀 503評(píng)論 0 2
  • 1、習(xí)慣 “人應(yīng)該支配習(xí)慣,而決不能讓習(xí)慣支配人,一個(gè)人不能去掉他的壞習(xí)慣,那簡(jiǎn)直一文不值。——奧斯特洛夫斯基 習(xí)...
    鄧成渝閱讀 236評(píng)論 2 0

友情鏈接更多精彩內(nèi)容