前言
大家周二好呀,.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 中是這樣的:

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)單的配置了下,是這樣的:

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)控制版本

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、查看效果

現(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)頭的效果

這個(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,自定義即可

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

這樣就能實(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ì)比
Your API versioning is wrong, which is why I decided to do it 3 different wrong ways