Dotnet Core多版本API共存的优雅实现
API升級,新舊版本的API共存,怎么管理呢?
?
一、前言
最近,單位APP做了升級,同步的,API也做了升級。
升級過程中,出現了一點問題:API升級后,舊API也需要保留,因為有舊的APP還在使用中。
那么,API端如何作到多個版本共存呢?
二、快速的解決辦法
API的露出,是在API的Route定義中實現的??聪旅娴睦?#xff1a;
[Route("api/[controller]")] public?class?DemoController?:?ControllerBase {[Route("demo")]public?ActionResult<T>?DemoFunc(){} }那我們知道,這個API的終結點是:/api/demo/demo。代碼中[controller]是個可替換變量,編譯時會替換為當前控制器的名稱。
這個Route,里面的參數是個字符串,也就是說是可以隨便換的。所以,對于多版本API,有個快速的辦法,就是在里面做文章。
我們可以寫成:
[Route("api/v1/[controller]")] public?class?DemoController?:?ControllerBase {[Route("demo")]public?ActionResult<T>?DemoFunc(){} }或者
[Route("api/[controller]")] public?class?DemoController?:?ControllerBase {[Route("v1/demo")]public?ActionResult<T>?DemoFunc(){} }這樣就區分出了版本號。
?
當然,這樣做比較LOW,因為版本號是硬編碼在代碼中的。而且,這個改動會影響到API的終結點,例如上面兩個變化,會讓終結點變為:/api/v1/demo/demo和/api/demo/v1/demo。如果前端可以方便修改,也算是一個方法。但對于我們APP已經上線運行來說,這個改動無法接受。
三、優雅的解決辦法
這個方案,才是今天要說的核心內容。
?
首先,我們需要從Nuget上引入兩個庫:
%?dotnet?add?package?Microsoft.AspNetCore.Mvc.Versioning %?dotnet?add?package?Microsoft.AspNetCore.Mvc.Versioning.ApiExplorer這兩個庫,Versioning用來實現API的版本控制,Versioning.ApiExplorer用來實現元數據的發現工作。
引入完成后,修改Startup.cs:
public?void?ConfigureServices(IServiceCollection?services) {services.AddApiVersioning(options?=>{options.DefaultApiVersion?=?new?ApiVersion(1,?0);options.AssumeDefaultVersionWhenUnspecified?=?true;options.ReportApiVersions?=?true;});services.AddVersionedApiExplorer(options?=>{options.GroupNameFormat?=?"'v'VVV";options.SubstituteApiVersionInUrl?=?true;});services.AddControllers(); }就可以了。
這里面用了兩個配置:AddApiVersioning,主要用來配置向前兼容,定義了如果沒有帶版本號的訪問,會默認訪問v1.0的接口。AddVersionedApiExplorer用來添加API的版本管理,并定義了版本號的格式化方式,以及兼容終結點上帶版本號的方式。
到這兒,引入版本管理的工作就完成了。
?
使用時,就直接在控制器或方法上定義版本號:
[ApiVersion("1")] [Route("api/[controller]")] public?class?DemoController?:?ControllerBase {[MapToApiVersion("2")][Route("demo")]public?ActionResult<T>?DemoFunc(){} }這里面,又是兩個屬性:ApiVersion定義控制器提供哪個版本的API。這個屬性可以定義多個。例如,我們控制器里既有v1的API,也有v2的API,我們可以寫成:
[ApiVersion("1")] [ApiVersion("2")] [Route("api/[controller]")] public?class?DemoController?:?ControllerBase { }MapToApiVersion是API的版本定義,定義我們這個API是哪一個版本。
方法就這么簡單。其它的,微軟都幫我們做好了。
?
那,通常我們會用Swagger來做API文檔。這個方法如何跟Swagger配合呢?
四、與Swagger的配合
Swagger也來自于Nuget的引用:
%?dotnet?add?package?swashbuckle.aspnetcore引用后,通常我們Startup.cs里的配置是這樣的:
public?void?ConfigureServices(IServiceCollection?services) {services.AddSwaggerGen(option?=>{option.SwaggerDoc("v1",?new?OpenApiInfo?{?Title?=?"Demo",?Version?=?"V1"?});});services.AddControllers(); } public?void?Configure(IApplicationBuilder?app,?IWebHostEnvironment?env) {app.UseSwagger();app.UseSwaggerUI(option?=>{option.SwaggerEndpoint("/swagger/v1/swagger.json",?"Demo");});}?
API多版本管理與Swagger配合,也有一個快速但比較LOW的方法:
public?void?ConfigureServices(IServiceCollection?services) {services.AddSwaggerGen(option?=>{option.SwaggerDoc("v1",?new?OpenApiInfo?{?Title?=?"Demo",?Version?=?"V1"?});option.SwaggerDoc("v1",?new?OpenApiInfo?{?Title?=?"Demo",?Version?=?"V2"?});});services.AddControllers(); } public?void?Configure(IApplicationBuilder?app,?IWebHostEnvironment?env) {app.UseSwagger();app.UseSwaggerUI(option?=>{option.SwaggerEndpoint("/swagger/v1/swagger.json",?"Demo?V1");option.SwaggerEndpoint("/swagger/v2/swagger.json",?"Demo?V2");}); }這個方法也可以快速實現,不過跟上邊的情況一樣,版本號是硬編碼的。
?
其實,也有另一個比較優雅的方式,就是手動實現IConfigureOptions<SwaggerGenOptions>和過濾IOperationFilter。
先看Startup.cs里:
public?void?ConfigureServices(IServiceCollection?services) {services.AddTransient<IConfigureOptions<SwaggerGenOptions>,?ConfigureSwaggerOptions>();services.AddSwaggerGen(options?=>?options.OperationFilter<SwaggerDefaultValues>());services.AddControllers(); } public?void?Configure(IApplicationBuilder?app,?IWebHostEnvironment?env) {app.UseSwagger();app.UseSwaggerUI(option?=>{foreach?(var?description?in?provider.ApiVersionDescriptions){c.SwaggerEndpoint($"/swagger/{description.GroupName}/swagger.json",?description.GroupName.ToUpperInvariant());}}); }這里加了兩個類,第一個ConfigureSwaggerOptions:
internal?class?ConfigureSwaggerOptions?:?IConfigureOptions<SwaggerGenOptions> {private?readonly?IApiVersionDescriptionProvider?_provider;public?ConfigureSwaggerOptions(IApiVersionDescriptionProvider?provider)?=>?_provider?=?provider;public?void?Configure(SwaggerGenOptions?options){foreach?(var?description?in?_provider.ApiVersionDescriptions){options.SwaggerDoc(description.GroupName,?CreateInfoForApiVersion(description));}}private?OpenApiInfo?CreateInfoForApiVersion(ApiVersionDescription?description){var?info?=?new?OpenApiInfo(){Title?=?"Demo?API",Version?=?description.ApiVersion.ToString(),};if?(description.IsDeprecated){info.Description?+=?"?方法被棄用.";}return?info;} }第二個SwaggerDefaultValues:
internal?class?SwaggerDefaultValues?:?IOperationFilter {public?void?Apply(OpenApiOperation?operation,?OperationFilterContext?context){var?apiDescription?=?context.ApiDescription;operation.Deprecated?|=?apiDescription.IsDeprecated();if?(operation.Parameters?==?null)return;foreach?(var?parameter?in?operation.Parameters){var?description?=?apiDescription.ParameterDescriptions.First(p?=>?p.Name?==?parameter.Name);if?(parameter.Description?==?null){parameter.Description?=?description.ModelMetadata?.Description;}if?(parameter.Schema.Default?==?null?&&?description.DefaultValue?!=?null){parameter.Schema.Default?=?new?OpenApiString(description.DefaultValue.ToString());}parameter.Required?|=?description.IsRequired;}} }代碼不一行行解釋了,都是比較簡單的。
?
運行,進入Swagger界面,右上角Select a definition,可以選擇我們定義的版本號。
?
今天的配套代碼已上傳到Github,位置在:https://github.com/humornif/Demo-Code/tree/master/0035/demo
喜歡就來個三連,讓更多人因你而受益
總結
以上是生活随笔為你收集整理的Dotnet Core多版本API共存的优雅实现的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 开源项目葫芦藤:IdentityServ
- 下一篇: Beetlex之websocket/tl