在上一篇的文章中,主要是搭建了咱們的開發環境,同時建立了咱們的項目模板框架。在整個先後端分離的項目中,後端的 API 接口相當重要,它是前端與後端之間進行溝通的媒介,如何構建一個 「好用」 的 API 接口,是須要咱們後端人員好好思考的。
在系統迭代的整個過程當中,不可避免的會添加新的資源,或是修改現有的資源,後端接口做爲暴露給外界的服務,變更的越小,對服務的使用方形成的印象就越小,所以,如何對咱們的 API 接口進行合適的版本控制,咱們勢必須要首先考慮。html
系列目錄地址:ASP.NET Core 項目實戰
倉儲地址:https://github.com/Lanesra712/Grapefruit.VuCore前端
項目老是在不斷迭代的,某些時候,由於業務發展的須要,須要將現有的接口進行升級,而原有的接口卻不能馬上中止使用。好比說,你開發了一個接口提供給愛啪啪 1.0 版本使用,後來愛啪啪的版本迭代了,須要接口返回的數據與原先 1.0 版本返回的數據不一樣了,這時候,接口確定是須要升級的,但是若是直接升級原有的接口,還在使用 1.0 版本的用戶不就 GG 了,所以,如何作到既可讓 1.0 版本的用戶使用,也可讓 2.0 版本的用戶使用就須要好好考慮了,常見的解決方案,主要有如下幾種。git
a)使用不一樣的 API 名稱github
最簡單粗暴,須要變動接口邏輯時就從新起個 API 名稱,新的版本調用新的 API 名稱,舊的版本調用舊的 API 名稱。json
https://yuiter.com/api/Secret/Login ##愛啪啪 1.0
https://yuiter.com/api/Secret/NewLogin ##愛啪啪 2.0
b)在 Url 中標明版本號後端
直接將 API 版本信息添加到請求的 Url 中,調用不一樣版本的 API ,就在 URL 中直接標明使用的是哪一個版本。api
https://yuiter.com/api/v1/Secret/Login ##愛啪啪 1.0
https://yuiter.com/api/v2/Secret/Login ##愛啪啪 2.0
c)請求參數中添加版本信息微信
將 API 的版本信息做爲請求的一個參數傳遞,經過指定參數值來肯定請求的 API 版本。markdown
https://yuiter.com/api/Secret/Login?version=1 ##愛啪啪 1.0
https://yuiter.com/api/Secret/Login?version=2 ##愛啪啪 2.0
d)在 header 中標明版本號app
前端在請求 API 接口時,在 header 中添加一個參數用來代表請求的版本信息,後端經過前端在 header 中設置的參數來判斷,從而執行不一樣的業務邏輯分支。
POST https://yuiter.com/api/Secret/Login
Host: yuiter.com
api-version: v1 ##愛啪啪 1.0
POST https://yuiter.com/api/Secret/Login
Host: yuiter.com
api-version: v2 ##愛啪啪 2.0
在 Grapefruit.VuCore 這個項目中,我選擇將 API 的版本信息添加到請求的地址中,從而明確的指出當前請求的接口版本信息。
後端完成了接口以後,確定須要告訴前端,不論是整理成 txt/excel/markdown 文檔,亦或是寫完一個接口就直接發微信告訴前端,老是要多作一步的事情,而 Swagger 則能夠幫咱們省去這一步。經過配置以後,Swagger 就能夠根據咱們的接口自動生成 API 的接口文檔,省時,省力。固然,若是前端小姐姐單身可撩,而你碰巧有意的話,另談。
Swagger 是一個能夠將接口文檔自動生成,同時能夠對接口功能進行測試的開源框架,在 ASP.NET Core 環境下,主流的有 Swashbuckle.AspNetCore 和 NSwag 這兩個開源框架幫助咱們生成 Swagger documents。這裏,我採用的是 Swashbuckle.AspNetCore。
在使用 Swashbuckle.AspNetCore 以前,首先咱們須要在 API(Grapefruit.WebApi) 項目中添加對於 Swashbuckle.AspNetCore 的引用。你能夠直接右鍵選中 API 項目選擇管理 Nuget 程序包進行加載引用,也能夠經過程序包管理控制檯進行添加引用,這裏注意,使用程序包管理控制檯時,你須要將默認的項目修改爲 API(Grapefruit.WebApi) 項目。當引用添加完成後,咱們就能夠在項目中配置 Swagger 了。
Install-Package Swashbuckle.AspNetCore
ASP.NET Core 的本質上能夠當作是一個控制檯程序,在咱們建立好的 ASP.NET Core Web API 項目中,存在着兩個類文件:Program.cs 以及 Startup.cs。與控制檯應用同樣,Program 類中的 Main 方法是整個程序的入口,在這個方法中,咱們將配置好的 IWebHostBuilder 對象,構建成 IWebHost 對象,並運行該 IWebHost 對象從而達到運行 Web 項目的做用。
在框架生成的 Program 類文件中,在配置 IWebHostBuilder 的過程時,框架默認爲咱們添加了一些服務,固然,這裏你能夠註釋掉默認的寫法,去本身建立一個 WebHostBuilder 對象。同時,對於一個 ASP.NET Core 程序來講,Startup 類是必須的(你能夠刪除生成的 Startup 類,從新建立一個新的類,可是,這個新建立的類必須包含 Configure 方法,以後只須要在 UseStartup<Startup> 中將該類配置爲 Startup 類便可),這裏若是不指定 Startup 類會致使啓動失敗。
在 Startup 類中,存在着 ConfigureServices 和 Configure 這兩個方法,在 ConfigureServices 方法中,咱們將自定義服務經過依賴注入的方式添加到 IServiceCollection 容器中,而這些容器中的服務,最終均可以在 Configure 方法中進行使用;而 Configure 方法則用於指定 ASP.NET Core 應用程序將如何響應每個 HTTP 請求,咱們能夠在這裏將咱們本身建立的中間件(Middleware)綁定到 IApplicationBuilder 上,從而添加到 HTTP 請求管道中。
這裏只是很粗略的說明了 ASP.NET Core 項目的啓動過程,想要仔細瞭解啓動過程的推薦園子裏的這篇文章 =》ASP.NET Core 2.0 : 七.一張圖看透啓動背後的祕密,由於 ASP.NET Core 2.1 版本相比於 2.0 版本又有些改變,這裏有一些不同的地方須要你去注意。
當咱們簡單瞭解了啓動過程後,就能夠配置咱們的 Swagger 了。Swashbuckle.AspNetCore 幫咱們構建好了使用 Swagger 的中間件,咱們只須要直接使用便可。
首先咱們須要在 ConfigureServices 方法中,將咱們的服務添加到 IServiceCollection 容器中,這裏,咱們須要爲生成的 Swagger Document 進行一些配置。
services.AddSwaggerGen(s => { s.SwaggerDoc("v1", new Info { Contact = new Contact { Name = "Danvic Wang", Email = "danvic96@hotmail.com", Url = "https://yuiter.com" }, Description = "A front-background project build by ASP.NET Core 2.1 and Vue", Title = "Grapefruit.VuCore", Version = "v1" }); });
以後,咱們就能夠在 Configure 方法中啓用咱們的 Swagger 中間件。
app.UseSwagger(); app.UseSwaggerUI(s => { s.SwaggerEndpoint("/swagger/v1/swagger.json", "Grapefruit.VuCore API V1.0"); });
此時,當你運行程序,在域名後面輸入/swagger 便可訪問到咱們的 API 文檔頁面。由於項目啓動時默認訪問的是咱們 api/values 的 GET 請求接口,這裏咱們能夠打開 Properties 下的 launchSetting.json 文件去配置咱們的程序默認打開頁面。
從上面的圖能夠看出,不論是使用 IIS 或是程序自託管,咱們默認打開的 Url 都是 api/values,這裏咱們將兩種啓動方式的 launchUrl 值都修改爲 swagger 以後再次運行咱們的項目,能夠發現,程序默認的打開頁面就會變成咱們的 API 文檔頁面。
咱們使用 API 文檔的目的,就是爲了讓前端知道請求的方法地址是什麼,須要傳遞什麼參數,而如今,並無辦法顯示出咱們對於參數以及方法的註釋,經過查看 Swashbuckle.AspNetCore 的 github 首頁能夠看到,咱們能夠經過配置,將生成的 json 文件中包含咱們對於 Controller or Action 的 Xml 註釋內容,從而達到顯示註釋信息的功能(最終呈現的 Swagger Doc 是根據以前咱們定義的這個 「/swagger/v1/swagger.json」 json 文件來生成的)。
右鍵咱們的 API 項目,屬性 =》生產,勾選上 XML 文檔文件,系統會默認幫咱們建立生成 XML 文件的地址,這時候,咱們從新生成項目,則會發現,當前項目下會多出這個 XML 文件。在從新生成項目的過程當中,你會發現,錯誤列表會顯示不少警告信息,提示咱們一些方法沒有添加 XML 註釋。若是你和我同樣強迫症的話,能夠把 1591 這個錯誤添加到上面的禁止顯示警告中,這樣就能夠再也不顯示這個警告了。
建立好 XML 的註釋文件後,咱們就能夠配置咱們的 Swagger 文檔,從而達到顯示註釋的功能。這裏,由於我會在 Grapefruit.Application 類庫中建立各類的 Dto 對象,而接口中是會調用到這些 Dto 對象的。所以,爲了顯示這些 Dto 上的註釋信息,這裏咱們也須要生成 Grapefruit.Application 項目的 XML 註釋文件。
PS:這裏我是將每一個項目生成的註釋信息 xml 文檔地址都放在了程序的基礎路徑下,若是你將 xml 文檔生成在別的位置,這裏獲取 xml 的方法就須要你進行修改。
services.AddSwaggerGen(s => { //... //Add comments description // var basePath = Path.GetDirectoryName(AppContext.BaseDirectory);//get application located directory var apiPath = Path.Combine(basePath, "Grapefruit.WebApi.xml"); var dtoPath = Path.Combine(basePath, "Grapefruit.Application.xml"); s.IncludeXmlComments(apiPath, true); s.IncludeXmlComments(dtoPath, true); });
當咱們把 Swagger 配置完成以後,咱們就能夠建立具備版本控制的 API 接口了。
在請求的 API Url 中標明版本號,我不知道你第一時間看到這個實現方式,會想到什麼,對於我來講,直接在路由信息中添加版本號不就能夠了。。。em,這樣過於投機取巧了。。。。
[Route("api/v1/[controller]")]//添加版本信息爲v1 [ApiController] public class ValuesController : ControllerBase { }
想了想,在 Url 中添加版本號,這個版本號是否是很像咱們在 MVC 中使用的 Area。
Area 是 MVC 中常用到的一個功能,咱們一般會將某些小的模塊拆分紅一個個的 Area,而這一個個的小 Area 其實就是這個 MVC 項目中的 MVC。經過爲 controller 和 action 添加另外一個路由參數 area,從而達到建立具備層次路由的結構。好比,這裏,咱們能夠建立一個 Area 叫 v1,用來存儲咱們 1.x 版本的 API 接口,以後若是有新的 API 版本,新增一個 Area 便可,是否是很簡單,嗯,說幹就幹。
右擊咱們的 API 項目,選擇添加區域,新增的 Area 名稱爲 v1。
當 ASP.NET Core 的腳手架程序添加完成 Area 後,則會打開一個文件提示咱們須要在 MVC 中間件中建立適用於 Area 的路由定義。
app.UseMvc(routes => { routes.MapRoute( name : "areas", template : "{area:exists}/{controller=Home}/{action=Index}/{id?}" ); });
當咱們添加好路由規則定義後,咱們在 Area 的 Controllers 文件夾下添加一個 WebAPI Controller。不一樣於 ASP.NET 中的 Area ,當咱們在 ASP.NET Core 建立好一個 Area 以後,腳手架生成的文件中再也不有 XXXAreaRegistration(XXX 爲 Area 的名稱)文件去註冊這個 Area,而咱們只須要在 Area 中的 Controller 中添加 Area 特性,便可告訴系統框架,這個 Controller 是在當前的 Area 下的。
若是你有本身嘗試的話,就會發現,當咱們建立好一個 v1 的 Area 後,這個請求的地址並無按照咱們的想法會體如今路由信息中,咱們最後仍是須要在 Route 中手動指明 API 版本。
[Area("v1")] [Route("api/v1/[controller]")] [ApiController] public class ValuesController : ControllerBase { }
這樣的話,和最開始直接在路由信息中寫死版本信息其實也就沒什麼差異了,上網搜了搜,發現巨硬爸爸,也早已爲咱們準備好了實現版本控制 API 的利器 - Microsoft.AspNetCore.Mvc.Versioning。
和上面使用 Swashbuckle.AspNetCore 的方式相同,在咱們使用 Versioning 以前,須要在咱們的 API 項目中添加對於該 dll 的引用。這裏須要注意下安裝的版本問題,由於 Grapefruit.VuCore 這個框架距離如今搭建也有幾個月的時間了,在這個月初的時候 .NET Core 2.2 也已經發布了,若是你和我同樣仍是採用的 .NET Core 2.1 版本的話,這裏安裝的 Versioning 版本最高只能到 2.3。
Install-Package Microsoft.AspNetCore.Mvc.Versioning
當咱們安裝完成以後,就能夠進行配置了。
public void ConfigureServices(IServiceCollection services) { services.AddApiVersioning(o => { o.ReportApiVersions = true;//return versions in a response header o.DefaultApiVersion = new ApiVersion(1, 0);//default version select o.AssumeDefaultVersionWhenUnspecified = true;//if not specifying an api version,show the default version }); }
ReportApiVersions:這個配置是可選的,當咱們設置爲 true 時,API 會在響應的 header 中返回版本信息。
DefaultApiVersion:指定在請求中未指明版本時要使用的默認 API 版本。這將默認版本爲1.0。
AssumeDefaultVersionWhenUnspecified:這個配置項將用於在沒有指明 API 版本的狀況下提供請求,默認狀況下,會請求默認版本的 API,例如,這裏就會請求 1.0 版本的 API。
這裏,刪除咱們以前的建立的 Area 和默認的 ValuesController,在 Controllers 文件夾下新增一個 v1 文件夾,將全部 v1 版本的 Controller 都建在這個目錄下。新建一個 Controller,添加上 ApiVersion Attribute 指明當前的版本信息。由於我採用的方案是在 Url 中指明 API 版本,因此,咱們還須要在 Route 中修改咱們的路由屬性以對應 API 的版本。這裏的 v 只是一個默認的慣例,你也能夠不添加。
[ApiVersion("1.0")] [Route("api/v{version:apiVersion}/[controller]")] [ApiController] public class VaulesController : ControllerBase { }
當咱們修改好咱們的 Controller 以後,運行咱們的項目,你會發現,API 文檔中顯示的請求地址是不對的,難道是咱們的配置沒起做用嗎?經過 Swagger 自帶的 API 測試工具測試下咱們的接口,原來這裏請求的 Url 中已經包含了咱們定義的版本信息,當咱們指定錯誤的版本信息時,工具也會告訴咱們這個版本的接口不存在。
雖然咱們請求的 Url 中已經帶上了版本信息,可是 API 文檔上顯示的請求地址倒是不許確的,強迫症,不能忍。這裏,須要咱們修改生成 Swagger 文檔的配置代碼,將路由中的版本信息進行替換。從新運行咱們的項目,能夠發現,文檔顯示的 Url 地址也已經正確了,自此,咱們建立帶有版本控制的 API 也就完成了。
public void ConfigureServices(IServiceCollection services) { services.AddSwaggerGen(s => { //... //Show the api version in url addresss.DocInclusionPredicate((version, apiDescription) =>
});
}
本章使用了 Microsoft.AspNetCore.Mvc.Versioning 這一組件來實現咱們對於 API 版本控制的功能實現,可能你會有疑問,咱們直接在路由中寫明版本信息不是更簡單嗎?在我看來,使用這一組件的目的,在於咱們能夠以多種的方式實現 API 版本控制的目的,若是哪天你不想在 Url 中指明版本信息後,你能夠很快的使用別的形式來完成 API 的版本控制。另外,直接在路由中寫上版本信息,是否是會顯得咱們比較 ‘low’,哈哈哈,開玩笑,最後祝你們聖誕快樂~~~