從壹開始前後端分離 [.netCore 填坑 ] 三十四║Swagger:API多版本控制,帶來的思考
前言
大家週二好呀,.net core + Vue 這一系列基本就到這裡差不多了,今天我又把整個系列的文章下邊的全部評論看了一下(我是不是很負責哈哈),提到的問題基本都解決了,還有一些問題,已經在QQ群裡討論過了,今天再寫一篇,然後給這個系列畫一個暫時的句號吧,這些天也考慮寫點兒啥,希望看到的小夥伴給點兒意見喲,其實我也是能力有限,不敢保證精通,不過只要想學,基本都能學到點兒東西的,至少至少能給大家在繁忙或者無聊的開發生涯中,多一點兒學習的動力吧,至少群裡邊的小夥伴是這樣(馬上破百了,快來加入我們吧),目前寫的都是淺顯的,打算下一步向架構師微服務方向簡單拓展下,這兩天簡單看了一下,真是雲裡來霧裡去,不是很通俗,還得自己啃,一起加油吧!!!
1、什麼是版本控制
這個詞語大家已經不會陌生,平時開發的時候,一定會用到過 Git 、SVN 或者 VSS (這三個我都用過,Git 應該是最好的),這個就是原始碼的版本控制。
來句官方定義:版本控制是指對軟體開發過程中各種程式程式碼、配置檔案及說明文件等檔案變更的管理,是軟體配置管理的核心思想之一。
那今天我們說的,就是 api介面的版本控制,這個大家一定也都接觸到了,在我們使用的 swagger 中是這樣的:
2、api版本控制的好處
簡單來說,介面是APP的重要組成部分,資料是APP的核心,介面是連線APP和資料的紐帶(這裡的 APP 是廣義上的介面呼叫者)。
一般情況下,我們專案中會有大量的介面,再加上版本的變化,介面的升級,一個介面,可能會有很多個稍有差異的介面,這個時候介面如果維護的不好,錯一個就是一大片,那我們對 api 進行版本控制的好處有:
(1)有助於保護原有系統,不受影響,並及時修復問題
(2)可以實現使用者的私人定製,(我之前接觸過付費介面,可以這個意思)。
(3)快速迭代。
之前我在開發的時候,倒是沒有考慮過這個問題,都是想當然的以為寫程式碼只有一個版本,亦或者根本就沒有版本概念,昨天晚上在看有一個小夥伴問到了 swagger 中,如何進行版本控制( 然後我想了想,在平時的開發中,我開發的專案中還沒有遇到過版本控制,都是 web 專案+控制檯專案,有問題就直接修改,有 bug 直接覆蓋那種,從來沒有考慮過版本,但是既然咱們這個系列是基於 api 介面的,版本應該是要有的,而且相信以後如果開發 api 專案的時候,也會遇到這個問題。我就研究了下 swagger 的原始碼,結合著網上的資料看了看,簡單的配置了下,是這樣的:
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 用來控制版本
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 = "[email protected]", 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、檢視效果
現在已經實現了,在 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、這個時候檢視效果,發現已經實現了我們檔案開頭的效果
這個時候效果已經實現了,但是這麼寫顯然不是很方便,首先,我們的組名 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,自定義即可
3、在兩個控制器中,新增相同的程式碼
這樣就能實現同名方法的版本控制了。
三、其他不適用於 swagger 的介面版本控制方法
這些方法我本打算寫下來,發現不能通過 swagger 展示,會報錯,只能通過 postman 測試,所以對我來說不是很完美,這裡把博友的文章貼出來,大家可以自己看一下。