原文作者:老張的哲學(xué)
1拯刁、什么是版本控制
這個詞語大家已經(jīng)不會陌生脊岳,平時開發(fā)的時候,一定會用到過 Git 垛玻、SVN 或者 VSS (這三個我都用過割捅,Git 應(yīng)該是最好的),這個就是源代碼的版本控制帚桩。
來句官方定義:版本控制是指對軟件開發(fā)過程中各種程序代碼亿驾、配置文件及說明文檔等文件變更的管理,是軟件配置管理的核心思想之一账嚎。
那今天我們說的莫瞬,就是 api接口的版本控制,這個大家一定也都接觸到了郭蕉,在我們使用的 swagger 中是這樣的:
2乏悄、api版本控制的好處
簡單來說,接口是APP的重要組成部分,數(shù)據(jù)是APP的核心恳不,接口是連接APP和數(shù)據(jù)的紐帶(這里的 APP 是廣義上的接口調(diào)用者)。
一般情況下开呐,我們項目中會有大量的接口烟勋,再加上版本的變化规求,接口的升級,一個接口卵惦,可能會有很多個稍有差異的接口阻肿,這個時候接口如果維護的不好,錯一個就是一大片沮尿,那我們對 api 進行版本控制的好處有:
(1)有助于保護原有系統(tǒng)丛塌,不受影響,并及時修復(fù)問題
(2)可以實現(xiàn)用戶的私人定制畜疾,(我之前接觸過付費接口赴邻,可以這個意思)。
(3)快速迭代啡捶。
之前我在開發(fā)的時候姥敛,倒是沒有考慮過這個問題,都是想當(dāng)然的以為寫代碼只有一個版本瞎暑,亦或者根本就沒有版本概念彤敛,昨天晚上在看有一個小伙伴問到了 swagger 中,如何進行版本控制( 然后我想了想了赌,在平時的開發(fā)中墨榄,我開發(fā)的項目中還沒有遇到過版本控制,都是 web 項目+控制臺項目勿她,有問題就直接修改袄秩,有 bug 直接覆蓋那種,從來沒有考慮過版本嫂拴,但是既然咱們這個系列是基于 api 接口的播揪,版本應(yīng)該是要有的,而且相信以后如果開發(fā) api 項目的時候筒狠,也會遇到這個問題猪狈。我就研究了下 swagger 的源碼,結(jié)合著網(wǎng)上的資料看了看辩恼,簡單的配置了下雇庙,是這樣的:
3、常見的版本控制有哪些灶伊?
通過上邊的配置疆前,我自認為很好的解決了這個問題,但是當(dāng)我深入學(xué)習(xí)的時候聘萨,發(fā)現(xiàn)并不是竹椒,比如如何很好的調(diào)用不同版本的接口?米辐,前端又如何對寫好的接口地址進行快速修改胸完?等等多個問題引起我的思考书释,通過搜索資料,我總結(jié)了以下赊窥,常見的版本控制有以下幾個方案:
0爆惧、直接修改方法名,比如:/api/blog_v1锨能,/api/blog_v2扯再,/api/blog_v3... 雖然有時候也用,不過我直接 pass
1址遇、通過路由控制熄阻,比如豆瓣:https://api.douban.com/v2/movie/in_theaters //本文重點說明,個人推薦傲隶,其他的大家可以參考博友文章
2饺律、通過參數(shù)選擇,比如:http://localhost:58427/api/Values?api-version=2.0
3跺株、通過http請求的 Headers 來控制复濒,接口地址不變,下邊會說到
4乒省、利用 content type 來控制
本文只是一個說明版本巧颈,并沒有把所有的方案都 code 出來,重點說了下路由控制袖扛,剩下的只是引導(dǎo)大家去思考這個問題砸泛,然后繼續(xù)學(xué)習(xí),畢竟會一兩個方法就行了蛆封,平時開發(fā)中唇礁,使用的并不是很頻繁,有好的想法歡迎下邊留言惨篱。
一盏筐、在 swagger 中通過路由實現(xiàn)版本控制
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 中,配置服務(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.reibang.com/u/94102b59cc2a" }
});
});
4平绩、修改 SwagerUI 調(diào)用配置
在 Configure 方法內(nèi)坤按,修改 app.UseSwaggerUI 如下:
app.UseSwaggerUI(c =>
{
//之前是寫死的
//c.SwaggerEndpoint("/swagger/v1/swagger.json", "ApiHelp V1");
//c.RoutePrefix = "";//路徑配置,設(shè)置為空馒过,表示直接在根域名(localhost:8001)訪問該文件
//根據(jù)版本名稱倒序 遍歷展示
typeof(ApiVersions).GetEnumNames().OrderByDescending(e => e).ToList().ForEach(version =>
{
c.SwaggerEndpoint($"/swagger/{version}/swagger.json", $"{ApiName} {version}");
});
});
5、查看效果
現(xiàn)在已經(jīng)實現(xiàn)了酗钞,在 swagger 中腹忽,進行多版本的展示,那要如何進行控制呢砚作,請往下看窘奏。
2、對接口進行版本配置
1葫录、剛剛我們已經(jīng)創(chuàng)建好了多版本的接口文檔着裹,那現(xiàn)在就需要配置接口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開發(fā)中,自帶的一個組管理面粮。
為什么要配置路徑呢少孝?是因為多版本的情況下,可能會出現(xiàn)重名函數(shù)熬苍,這里沒有體現(xiàn)出來稍走,因為使用的是 :V2_Blogtest ,下邊的文章中會說到柴底,如果一定要重名婿脸,需要怎么做。
2柄驻、這個時候查看效果狐树,發(fā)現(xiàn)已經(jīng)實現(xiàn)了我們文件開頭的效果
這個時候效果已經(jīng)實現(xiàn)了,但是這么寫顯然不是很方便凿歼,首先褪迟,我們的組名 GroupName 是寫死的 ”v2“,不利用拓展答憔,然后呢味赃,還需要再一次配置路由 Route,有小伙伴就發(fā)現(xiàn)了虐拓,既然這兩個都是特性心俗,有沒有辦法重寫一個特性,把這兩個合并呢,欸城榛?揪利!就是這樣,請往下看狠持。
3疟位、自定義路由特性,實現(xiàn)路由+版本 雙控制
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>
/// 分組名稱,是來實現(xiàn)接口 IApiDescriptionGroupNameProvider
/// </summary>
public string GroupName { get; set; }
/// <summary>
/// 自定義路由構(gòu)造函數(shù),繼承基類路由
/// </summary>
/// <param name="actionName"></param>
public CustomRouteAttribute(string actionName = "[action]") : base("/api/{version}/[controller]/" + actionName)
{
}
/// <summary>
/// 自定義版本+路由構(gòu)造函數(shù)正勒,繼承基類路由
/// </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 接口進行設(shè)置
/// <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接口版本控制已經(jīng)說完了里覆,采用的方法是路由控制丧荐,我個人感覺還是挺好的,當(dāng)然文章的開頭也說到了喧枷,還是有其他的方法虹统,這里就簡單的其中一個,個人不是很推薦隧甚,但是大家可以看看车荔。
二、同名接口的版本控制
在上邊咱們說到了戚扳,如果兩個版本的方法名一定要一直咋辦呢忧便,重載大家肯定都知道,但是同一個 controller 接口方法肯定無論參數(shù)還是名稱全部都一樣帽借,就連返回類型也一樣珠增,所以不能重載,那我們應(yīng)該怎么辦呢砍艾?蒂教,請往下看。
1脆荷、 在 controller 文件夾下凝垛,新建兩個文件夾懊悯, v1、v2
2梦皮、然后添加相同的接口控制器 ApbController.cs炭分,自定義即可
3、在兩個控制器中剑肯,添加相同的代碼
這樣就能實現(xiàn)同名方法的版本控制了捧毛。
三、其他不適用于 swagger 的接口版本控制方法
這些方法我本打算寫下來让网,發(fā)現(xiàn)不能通過 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