Swagger多版本控制實現
最近前後端分離的項目越來越多,API的對接對於前後端開發交流得最多的一塊內容,一個好的API文檔生成工具就顯得非常重要,選取了Swagger文檔生成工具作為項目的文檔生成工具,考慮到多版本的文檔生成,基於swagger5.6版本實現了一套多版本控制
1. NuGet包引用
比較簡單,下面三個包都引用,刪掉自動生成的頁面文件,本此介紹多版本此處省略NuGet引入的文件清理工作了
2. 多版本控制
找到App_Start文件夾下面的SwaggerConfig,找到下圖的節點,默認是c.SingApiVersion生效,先註釋此行,再打開c.MultipleApiVersions勾子節點
此處有一個匿名方法調用ResolveVersionSupportByRouteConstraint,此方法需要自己實現,其中apiDesc是當前工程文件實現ApiController的全部路由數據,targetApiVersion是當前版本信息,直接在SwaggerConfig類中生成私有方法,我的實現如下,思路是在此處過濾掉非當前版本的Controller路由
private static bool ResolveVersionSupportByRouteConstraint(ApiDescription apiDesc, string targetApiVersion) {//過濾由多版本的controller帶來的重復route註冊api desc,按命名空間的版本信息過濾,只返回版本內的api return apiDesc.ActionDescriptor.ControllerDescriptor.ControllerType.FullName.ToLower().Contains(string.Format(".{0}.", targetApiVersion)); }
3. 路由文件版本分離規則
註意工程文件中Controllers的版本是通過命名空間隔離的,上面的代碼就是通過版本信息來匹配這個命名空間來實現過濾,假設v1和v2文件夾中都有ValuesController
兩個版本的ValuesController如下,route在Map時不能有重復名稱的Controller,所以此處采取加上版本,v1不需要添加,v2的ValuesController上加上版本, 借助RoutePrefix為ValuesV2Controller重寫路由,對外路由統一為api/{verstion}/Values並不會有ValuesV2出現在api路由上,此處的ValuesV2在文檔生成時還需要特殊處理,下面會重點提到IDocumentFilter接口實現時的處理
4. 多版本路由註冊
此時在WebApiConfig的路由註冊中寫下如下代碼,分別註冊兩個版本的路由對應v1,v2版本
config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/v1/{controller}/{id}", defaults: new { id = RouteParameter.Optional} ); config.Routes.MapHttpRoute( name: "Apiv2", routeTemplate: "api/v2/{controller}/{id}", defaults: new { id = RouteParameter.Optional } );
5. IDocumentFilter接口實現
多版本的控制到這就算基本實現了,只不過ValuesV2這個路由的文檔中依然會帶上這個V2, 由於我們在RoutePrefix中已經配置了V2的Values路由是api/v2/Values,但按路由註冊規則文檔生成的路由默認會是api/v2/ValuesV2, 如果按文檔生成的路徑調用時會報找不到文件,原因就是RoutePrefix生效的路由與Swagger文檔路由不匹配,好在Swagger提供的過濾接口能幫助我們自定義過濾規則,在SwaggerConfig中找到如下節點,打開註釋,其中ApplyDocumentVendorExtensions需要我們自己實現
c.DocumentFilter<ApplyDocumentVendorExtensions>();
實現IDocumentFilter接口的ApplyDocumentVendorExtensions文檔處理類
internal class ApplyDocumentVendorExtensions : IDocumentFilter { /// <summary> /// //swagger版本控制過濾 /// </summary> /// <param name="swaggerDoc">文檔</param> /// <param name="schemaRegistry">schema註冊</param> /// <param name="apiExplorer">api概覽</param> public void Apply(SwaggerDocument swaggerDoc, SchemaRegistry schemaRegistry, IApiExplorer apiExplorer) { //緩存目標路由api IDictionary<string, PathItem> match = new Dictionary<string, PathItem>(); //取版本 var version = swaggerDoc.info.version; foreach (var path in swaggerDoc.paths) { //過濾命名空間 按名稱空間區分版本 if (path.Key.Contains(string.Format("/{0}/", version))) { //匹配controller descript中的版本信息 Regex r = new Regex("/\\w+" + version, RegexOptions.IgnoreCase); string newKey = path.Key; if (r.IsMatch(path.Key)) { var routeinfo = r.Match(path.Key).Value; //修正controller別名路由符合RoutePrefix配置的路由 如api/v2/ValuesV2 修正為 api/v2/Values newKey = path.Key.Replace(routeinfo, routeinfo.Replace(version.ToLower(), "")).Replace( routeinfo, routeinfo.Replace(version.ToUpper(), "")); } //保存修正的path match.Add(newKey, path.Value); } } //當前版本的swagger document swaggerDoc.paths = match; } }
至此多版本控制就實現了,其中要註意的地方是Controllers下的V1,V2文件夾下的Controller文件的命名空間要保持默認命名空間,效果如下
Swagger多版本控制實現