程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> ASP.NET WebApi 文檔Swagger中度優化,webapiswagger

ASP.NET WebApi 文檔Swagger中度優化,webapiswagger

編輯:關於.NET

ASP.NET WebApi 文檔Swagger中度優化,webapiswagger


本文版權歸博客園和作者吳雙本人共同所有,轉載和爬蟲請注明原文地址: www.cnblogs.com/tdws

  寫在前面

在後台接口開發中,接口文檔是必不可少的。在復雜的業務當中和多人對接的情況下,簡單的接口文檔又不能滿足需求,試想你的單應用後台有幾十個模塊,幾百甚至更多的接口,又有上百個ViewModel。怎麼能讓人用起來更順手更明了?本篇介紹第一步的中度優化,下一篇將分享下一階段的深度優化。

 第一篇:ASP.NET WebApi 文檔Swagger中度優化

1.上手使用

2.Controller注釋讀取和漢化

3.Actionf group by 分組

4.通過exe整合xxxModel.xml和xxxAPI.XML

5.通過批處理命令在生成後調用exe

 第二篇:ASP.NET WebApi 文檔Swashbuckle.Core與SwaggerUI深度定制

Swagger是一款完全開源的文檔工具,其優點在於前後端的完整分離,他們之間的契約就是Json的數據格式。其後台項目就是github中的Swashbuckle。其前台項目就是github中的SwaggerUI。有一點需要注意的是,如果你直接從nuget安裝Swashbuckle的話,你也並不想做更多的定制化,那麼UI界面你完全不需要處理,因為所有的資源Resources都是嵌入到Swashbuckle.dll當中的,你可以在vs對象管理器中查看到Resources,如下圖,是不是又復習了dll的作用了呢?其中還可以包含css,js,image等資源:

看下本次分享的效果圖吧,只選了四個Controller做展示,個人覺得還是比較明了的吧,如果模塊和控制器多了起來,就會深刻體會到好處:

 

  先學會上手使用

  nuget中搜索Swashbuckle並安裝到你的WebApi項目中,其他的不用安裝哦。

  安裝完成後你的App_Start中會多一個SwaggerConfig這樣一個配置文件,這個文件是Swagger為我們留下的配置入口,我們可以根據其中的注釋和介紹做很多事情。然後我把Swagger單獨出來一個文件夾,直接將配置類拖進去,如下效果:

下一步配置你的項目屬性,在其生成選項當中,選擇下圖配置:

配置文件中主要有兩個入口:EnableSagger和EnableSwaggerUi,顧名思義,配置其後台項和前台項。

找到下面這行代碼,傳入你所需的兩個字符串

下一步找到IncludeXmlComments方法,配置讀取XML的路徑:

 c.IncludeXmlComments(string.Format("{0}/bin/SwaggerDemo.XML", System.AppDomain.CurrentDomain.BaseDirectory));

至此基本就可以顯示你的接口了,請訪問:localhost:xxxx/swagger   你可能會問為什麼我沒有添加頁面也能展示,這就是因為頁面和其路由設置都在dll中了!下面這段源碼揭示了為什麼可以直接通過路由訪問到你的swagger主頁,當然你也可以不必關心下面這段:

public void EnableSwaggerUi(
            string routeTemplate,
            Action<SwaggerUiConfig> configure = null)
        {
            var config = new SwaggerUiConfig(_discoveryPaths, _rootUrlResolver);
            if (configure != null) configure(config);

            _httpConfig.Routes.MapHttpRoute(
                name: "swagger_ui" + routeTemplate,
                routeTemplate: routeTemplate,
                defaults: null,
                constraints: new { assetPath = @".+" },
                handler: new SwaggerUiHandler(config)
            );

            if (routeTemplate == DefaultRouteTemplate)
            {
                _httpConfig.Routes.MapHttpRoute(
                    name: "swagger_ui_shortcut",
                    routeTemplate: "swagger",
                    defaults: null,
                    constraints: new { uriResolution = new HttpRouteDirectionConstraint(HttpRouteDirection.UriResolution) },
                    handler: new RedirectHandler(_rootUrlResolver, "swagger/ui/index"));
            }
        }

 

 

 Controller注釋讀取

默認情況下,Controller注釋是沒有讀取的。那麼你需要通過如下配置來達到目的,增加這樣一個類,不用問為什麼。下面這段代碼也是參考了一位園友的。漢化我也沒必要講了,他已經說的很詳細了。

 
public class CachingSwaggerProvider : ISwaggerProvider
    {
        private static ConcurrentDictionary<string, SwaggerDocument> _cache =
            new ConcurrentDictionary<string, SwaggerDocument>();

        private readonly ISwaggerProvider _swaggerProvider;

        public CachingSwaggerProvider(ISwaggerProvider swaggerProvider)
        {
            _swaggerProvider = swaggerProvider;
        }

        public SwaggerDocument GetSwagger(string rootUrl, string apiVersion)
        {
            var cacheKey = String.Format("{0}_{1}", rootUrl, apiVersion);
            SwaggerDocument srcDoc = null;
            //只讀取一次
            if (!_cache.TryGetValue(cacheKey, out srcDoc))
            {

                //AppendModelToCurrentXml();
                srcDoc = _swaggerProvider.GetSwagger(rootUrl, apiVersion);
                srcDoc.vendorExtensions = new Dictionary<string, object> { { "ControllerDesc", GetControllerDesc() }, { "", "" } };
                _cache.TryAdd(cacheKey, srcDoc);
            }
            return srcDoc;
        }

        /// <summary>
        /// 從API文檔中讀取控制器描述
        /// </summary>
        /// <returns>所有控制器描述</returns>
        public static ConcurrentDictionary<string, string> GetControllerDesc()
        {
            string xmlpath = String.Format("{0}/bin/SwaggerDemo.XML", AppDomain.CurrentDomain.BaseDirectory);
            ConcurrentDictionary<string, string> controllerDescDict = new ConcurrentDictionary<string, string>();
            if (File.Exists(xmlpath))
            {
                XmlDocument xmldoc = new XmlDocument();
                xmldoc.Load(xmlpath);
                string type = String.Empty, path = String.Empty, controllerName = String.Empty;

                string[] arrPath;
                int length = -1, cCount = "Controller".Length;
                XmlNode summaryNode = null;
                foreach (XmlNode node in xmldoc.SelectNodes("//member"))
                {
                    type = node.Attributes["name"].Value;
                    if (type.StartsWith("T:"))
                    {
                        //控制器
                        arrPath = type.Split('.');
                        length = arrPath.Length;
                        controllerName = arrPath[length - 1];
                        if (controllerName.EndsWith("Controller"))
                        {
                            //獲取控制器注釋
                            summaryNode = node.SelectSingleNode("summary");
                            string key = controllerName.Remove(controllerName.Length - cCount, cCount);
                            if (summaryNode != null && !String.IsNullOrEmpty(summaryNode.InnerText) && !controllerDescDict.ContainsKey(key))
                            {
                                controllerDescDict.TryAdd(key, summaryNode.InnerText.Trim());
                            }
                        }
                    }
                }
            }
            return controllerDescDict;
        }
    }

並且在SwaggerConfig找到下面這行代碼:

 c.CustomProvider((defaultProvider) => new CachingSwaggerProvider(defaultProvider));

至此XML讀取完成。

  Action分組展示

下面這個功能通過我們自定義的Attribute來實現。先往下看,不用問為什麼,功能實現後自然明白啦:

 /// <summary>
    /// Controller描述信息
    /// </summary>
    [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
    public class ControllerGroupAttribute : Attribute
    {
        /// <summary>
        /// 當前Controller所屬模塊 請用中文
        /// </summary>
        public string GroupName { get; private set; }

        /// <summary>
        /// 當前controller用途    請用中文
        /// </summary>
        public string Useage { get; private set; }

        /// <summary>
        ///  Controller描述信息 構造
        /// </summary>
        /// <param name="groupName">模塊名稱</param>
        /// <param name="useage">當前controller用途</param>
        public ControllerGroupAttribute(string groupName, string useage)
        {
            if (string.IsNullOrEmpty(groupName) || string.IsNullOrEmpty(useage))
            {
                throw new ArgumentNullException("groupName||useage");
            }
            GroupName = groupName;
            Useage = useage;
        }
    }

給你的每個Controller加上這個Attribute:

 

 

為了分模塊,我做了這種描述,其他內容不一一描述:

在Swagger找到GroupActionsBy方法:

並且按照你所設定的分組Attribute來分組排序,最終達到我們想要的效果,請仔細閱讀代碼,就自然理解了:

 c.GroupActionsBy(apiDesc => apiDesc.GetControllerAndActionAttributes<ControllerGroupAttribute>().Any() ? 
apiDesc.GetControllerAndActionAttributes<ControllerGroupAttribute>().First().GroupName + "_" +
apiDesc.GetControllerAndActionAttributes<ControllerGroupAttribute>().First().Useage : "無模塊歸類");
   合並Model層的XML文件

上面的效果,你可能看不到Model層的描述信息(如果你的實體沒有在其他層是會正常顯示的)。但是Model分層了,怎麼能展示呢,我目前給出的解決方案是合並XML.寫了一個ConsoleApp,當然你也可以手動來復制處理。

通過相對路徑,將model層的xml生成到與API層相同的路徑下,方便我們來處理。ConsoleApp的代碼如下

static void Main(string[] args)
        {
            AppendModelToCurrentXml();
            Console.WriteLine(AppDomain.CurrentDomain.BaseDirectory);
            Console.WriteLine("XML整合成功");
            //Console.ReadLine();
        }

        public static void AppendModelToCurrentXml()
        {
            XmlDocument xmlDocument = new XmlDocument();
            xmlDocument.Load(AppDomain.CurrentDomain.BaseDirectory + @"/bin/Model.XML");
            var membersNode = xmlDocument.GetElementsByTagName("members")[0]; //Model層Members節點
            xmlDocument.Load(AppDomain.CurrentDomain.BaseDirectory + @"/bin/SwaggerDemo.XML");
            var currentMembersNode = xmlDocument.GetElementsByTagName("members")[0];  //API層Members節點
            for (int i = 0; i < membersNode.ChildNodes.Count; i++)
            {
                var memberNode = membersNode.ChildNodes[i];
                currentMembersNode.AppendChild(memberNode.Clone());
                if (memberNode.HasChildNodes)
                {
                    memberNode.AppendChild(memberNode.ChildNodes[0]);
                }
            }
            xmlDocument.Save(AppDomain.CurrentDomain.BaseDirectory + @"/bin/SwaggerDemo.XML");
        }

至於我為什麼要取路徑時選擇appDomain,是因為我准備把該exe文件放置到webAPI項目的根目錄下,這樣取到的路徑就是API項目的物理路徑,如果把物理路徑寫死,很煩的,你懂的,因為發布或者到其他人電腦中,物理路徑基本就變了。這段代碼的主要功能就是把Model層中所生成的XML中的Members節點的所有內容,Copy到API項目的XML當中。

 並且,為了不用每次都手動調用exe文件,我使用批處理命令,在每次項目生成後,自動執行exe.

Swagger的前後端分離,靠json格式的契約,你可以在network中查看json結果,下一篇深度定制分享將會用到它。

 

 

   寫在最後

 本篇多數是配置化的內容,只不過在實際操作的時候,面對不同情況,我們希望能得到更好的結果。動手嘗試一下吧,如果有問題和不足的地方,歡迎留言探討,萬一你以後用上了呢,不,應該說WebApi文檔你是一定用得上。下一篇,將會更深一步的優化,讓我們拿到源碼後,無論有什麼特別的需求,都能自己親手修改,而不被蒙蔽在dll中。

如果我的點滴分享對你有點滴幫助,歡迎點下方紅色按鈕關注,我將持續分享。也歡迎為我,也為你自己點贊。

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved