1. 程式人生 > >Swagger多版本控制實現

Swagger多版本控制實現

並不會 parameter 保持 bsp efault class nuget urn ati

最近前後端分離的項目越來越多,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多版本控制實現