请原谅我这个标题党,写到了第100篇随笔,说是深度优化,其实也并没有什么深度。源码也没怎么修改,如果你想使用WebApi Swagger文档,请先移步到上一篇的中度优化。
第一篇:ASP.NET WebApi 文档Swagger中度优化
第二篇:ASP.NET WebApi 文档Swashbuckle.Core与SwaggerUI深度定制
1.文档前后端分离
2.项目结构
3.实现分Area(模块)来筛选所需展示的Controller
文档前后端分离
本篇文章提供源码,下载链接: 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; }