zoukankan      html  css  js  c++  java
  • ASP.NET WebApi 文档Swagger深度优化

    本文版权归博客园和作者吴双本人共同所有,转载和爬虫请注明博客园蜗牛原文地址,cnblogs.com/tdws

      写在前面

    请原谅我这个标题党,写到了第100篇随笔,说是深度优化,其实也并没有什么深度。源码也没怎么修改,如果你想使用WebApi Swagger文档,请先移步到上一篇的中度优化。

     第一篇:ASP.NET WebApi 文档Swagger中度优化 http://www.cnblogs.com/tdws/p/6100126.html

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

    1.文档前后端分离

    2.项目结构

    3.实现分Area(模块)来筛选所需展示的Controller

     第三篇  :ASP.NET WebApi 文档增加登录 http://www.cnblogs.com/tdws/p/7395556.html

      文档前后端分离

    本篇文章提供源码,下载链接:  https://github.com/coderws/CustomSwagger  (由于博客园只能上传10MB,就放github上吧,不求star,只为了方便大家下载)

       由于Swagger原版的swagger页面和资源js css等文件被嵌入到dll当中,虽然我们可以找到swagger UI的源码,但用起来依然感到有些不便。所以使用了github上另一个开源项目sosoapi,它的文档功能也是基于swagger的,不过为我们做好了一定得拓展,比如现成的中英切换一类的。并且资源完全与之前的嵌入性资源不同,直接分离出来一个swagger UI的文件夹,包括js,css,images,html等等,这样的话,我们修改起来很方便呀,可以做到你想要的定制化,并且我将swagger的后台处理源码down下来,加到解决方案当中,以后遇到问题,再也不用收到dll不能修改和新增功能的困扰。可以将swagger作为你框架中的一部分。

      项目结构

    你可以看到Swashbuckle.Core。就是Swagger的后台源码,展开Test项目,其中的Doc文件夹,就是全部的SwaggerUI的内容啦,index.html就是我们的文档首页:

      实现分Area(模块)来筛选所需展示的Controller

      再说Area分组前提一点小的改动

      Required为醒目红色

    Swagger 最大宽度增加到1100px,

    左侧增加模块筛选,点击筛选后,将只显示对应模块下。

    具体的实现方式,就是根据你的模块名称,来筛选api访问路径

    如上图第一个user,就代表user模块了,第二个user才是控制器名称。这样的定义,使用了MVC5的新特性RouteAttribute和RoutePrefixAttribute,他们用于在controller中自定义路由,你可以下载源码看到。

    那我们左侧菜单栏的数据是哪儿来的呢。请看源码SwaggerConfig中,我定义了这样一个方法:

    /// <summary>
            /// 从API文档中读取控制器描述
            /// </summary>
            /// <returns>所有控制器描述</returns>
            public static ConcurrentDictionary<string, string> GetControllerDesc(HashSet<string> moduleList)
            {
                string xmlpath = String.Format("{0}/bin/SwaggerCustom.Test.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"))
                            {
                                //模块信息
                                var moduleName = arrPath[length - 2];
                                moduleList.Add(moduleName);
    
                                //获取控制器注释
                                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;
            }

    本来这个方法仅用于读取Controller注释的,但是既然这里读取了xml,我也就没必要再读一遍了。我增加了一个HashSet<string>类型的参数,在集合中数据每一个元素的数据是唯一的,所以使用它来存模块信息,xml每次遍历到Controller的时候,我都会将其Modules信息读取出来(比如namespace SwaggerCustom.Test.Controllers.Admin.AdminRoleController ),那么倒数第二位的单词就是模块名称。这样一来我们的模块信息也包含在了json中,看下图AreaDescription,他说明了我有三个模块。

    那么接下来就是要做页面渲染前的筛选工作了。我首先猜测渲染一定和返回json中的path(接口路径)有关,所以我在swagger-ui-ext.js中搜索path,总算找到了结果:

    如下所示:遍历时,通过筛选Module模块,对不符合我所想要的module进行直接return,而不向下执行,最终达到想要的目的。之所以说我没有做深度优化的原因也是这里,真正的深度优化应该是在后台代码中所筛选,只返回自己想要的json结果,偷懒了。

    所以下图,就是我点击Admin选项后所筛选的结果,该模块下只有两个Controller。

    有关我个人所增加的js代码,在Doc/lib/swagger-custom.js中。

    //获取Controller描述
    function SwaggerCustom() {
        this.setControllerSummary = function () {
            $.ajax({
                type: "get",
                async: true,
                url: $("#input_baseUrl").val(),
                dataType: "json",
                success: function (data) {
                    var summaryDict = data.ControllerDesc;
                    var id, controllerName, strSummary;
                    $("#resources_container .resource").each(function (i, item) {
                        id = $(item).attr("id");
                        if (id) {
                            controllerName = id.substring(9);
                            strSummary = summaryDict[controllerName];
                            if (strSummary) {
                                $(item).children(".heading").children(".options").prepend('<li style="color:red;" class="controller-summary" title="' + strSummary + '">' + strSummary + '</li>');
                            }
                        }
                    });
                    swaggerCustom.loadMenu(data.AreaDescription);
                    expendtoggle();//注册菜单收缩事件
                }
            });
    
        };
        //获取当前参数
        this.getQueryString = function (name) {
            var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)");
            var r = window.location.search.substr(1).match(reg);
            if (r != null) return unescape(r[2]); return null;
        },
        this.loadMenu = function (modules) {
            var url = window.location.href;
            url = url.substring(0, url.lastIndexOf('?'));
            $('body').prepend('<div class="menu-expender" id="menuExpend">←</div><div id="moduleMenu"><div onclick="menuClick(this)" data-url="' + url + '?module=all"  " data-module="all" class="menu-inner" >全部</div></div>');
            var menuInner = '';
            modules.forEach(function (item) {
                menuInner += '<div onclick="menuClick(this)" data-url="' + url + '?module=' + item.toLowerCase() + '" data-module="' + item.toLowerCase() + '" class="menu-inner" >' + item + '</div>';
            });
            $('#moduleMenu').append(menuInner);
            $('#moduleMenu').css("position", "fixed").css("top", "20%");
        }
    }
    var swaggerCustom = new SwaggerCustom();
    var swaggerCustomGlobalData = {
        currentModule: "all"
    }
    $(function () {
        swaggerCustomGlobalData.currentModule = swaggerCustom.getQueryString('module') == null ? "all" : swaggerCustom.getQueryString('module');
        //alert(swaggerCustomGlobalData.currentModule);
    });
    var swaggerStyle = {
        showActionLink: function () {
            $("li .toggleEndpointList").css("color", "#2392f7");
        },
        titleStyle: function () {
            $("h2 .toggleEndpointList").css("color", "green");
        },
        showDetailLink: function () {
            $("li .expandResource").css('color', '#996633');
        },
        paramTable: function () {
            $('.fullwidth.parameters thead tr th:nth-child(1)').width('50px');
            $('.fullwidth.parameters thead tr th:nth-child(2)').width('350px');
            $('.fullwidth.parameters thead tr th:nth-child(3)').width('100px');
            $('.fullwidth.parameters thead tr th:nth-child(4)').width('60px');
            $('.fullwidth.parameters thead tr th:nth-child(5)').width('400px');
            $('td textarea').width('380px');
        },
        init: function () {
            this.showActionLink();
            this.titleStyle();
            this.showDetailLink();
            //this.paramTable();
        }
    }
    
    function menuClick(ele) {
        window.location.href = (ele.dataset.url);
    }
    function expendtoggle() {
        $('#menuExpend').toggle(function () {
            $(this).html('→');
            $('#moduleMenu').hide();
        }, function () {
            $(this).html('←');
            $('#moduleMenu').show();
        });
    }
      该如何使用??

      使用直接copy走SwaggerConfig.cs和Doc文件放到你项目下,并且引用Swashbuckle就没问题(可以直接拿源码类库,也可以直接从nuget上安装)。并且你想使用这个模块划分的功能,就一定要符合路由规则api/modulename/controller/action.因为模块的筛选就是通过modulename嘛。如果你只想使用swagger的基本功能,看上一篇的分享就足够了。拿到本次分享的源码,你就可以在其中合理的修改源码,自定义你的功能

      写在最后

    2017-03-13补充几点

    这个处理结果的展示,通过在action的描述文档注释上 ,增加节点<response code="201">Returns the newly created item</response>  

    Swagger不识别ActionResult,需要确定返回类型,所以在Core中 需要增加特性标签 

    依然请原谅我这个标题党。

    如果我的点滴分享对你有点滴帮助,欢迎点下方红色按钮关注,我将持续输出分享。也欢迎为我,也为你自己点赞。关于本篇源码有任何问题,欢迎留言。

  • 相关阅读:
    STDMETHOD (转)
    DirectX中的纹理映射相关技术 (转)
    (转)清空std::stringstream,联系到stream的clear()和清空
    (转载)MultiAnimation
    (转)SkyBox
    [转载]漫谈游戏中的阴影技术
    反射矩阵计算
    (转)COM组件里的AddRef()
    LINQ简记(2):重要概念
    继续聊WPF——自定义命令
  • 原文地址:https://www.cnblogs.com/tdws/p/6103289.html
Copyright © 2011-2022 走看看