最近做的项目使用mvc+webapi,采取前后端分离的方式,后台提供API接口给前端开发人员。这个过程中遇到一个问题后台开发人员怎么提供接口说明文档给前端开发人员,最初打算使用word文档方式进行交流,实际操作中却很少动手去写。为了解决这个问题,特意在博客园中搜索了一下api接口文档生成的文章,引起我注意的有两种方案。1.微软自带的Microsoft.AspNet.WebApi.HelpPage  2.swagger(我比较喜欢戏称为“丝袜哥”)

最先尝试的是微软自带的方案,由于项目对webapi了一定改造导致使用该方案时一直报错,于是转向了第二种方案,经过大半天大捣鼓,最终效果如下

1.列出所有API控制器和控制器描述

2.列出action和描述

3.直观的接口测试

达到这几点目标,已经满足项目使用。

阅读目录

  • 使用swagger
  • 汉化及问题解决
  • ApiExplorer思路拓展
  • 总结
回到顶部

使用swagger

  1.创建webapi项目解决方案

  2.引用swagger nuget包

  Swashbuckle和Swagger.Net.UI两个包

  3.卸载重复包Swagger.Net

  引用Swagger.Net.UI时会引用Swagger.Net这个包,但是Swagger.Net的功能和Swashbuckle重复了。所以我采取了卸载Swagger.Net

 删除多余的SwaggerUI文件夹

删除多余的配置类SwaggerNet

4.添加接口注释

完成上面三部运行项目,可以看到接口描述已经生成,浏览地址http://xxx/Swagger。但是没有接口的注释,下面添加接口注释

 项目属性->勾选生成xml文档文件

修改SwaggerConfig文件

    //c.IncludeXmlComments(GetXmlCommentsPath());//设置接口描述xml路径地址c.IncludeXmlComments(string.Format("{0}/bin/SwaggerDemo.XML", System.AppDomain.CurrentDomain.BaseDirectory));

给接口添加注释,即可看到参数及方法描述了
回到顶部

汉化及问题解决

经过上面的操作,已经完成了所需功能。但是还有几点问题需要完善

1.界面的说明都是英文的需要进行汉化

2.控制器没有描述

3.接口过多每次生成速度比较慢

1.汉化步骤

在SwaggerConfig配置文件中有这么一段代码

 .EnableSwaggerUi(c =>{     //c.InjectJavaScript(thisAssembly, "Swashbuckle.Dummy.SwaggerExtensions.testScript1.js") });

这段代码的作用是向页面输出引用Swashbuckle.Dummy.SwaggerExtensions.testScript1.js文件,或许会疑问js文件路径为什么这么奇怪。那是因为Swagger将资源文件都嵌入到dll中了,我们常用的资源文件都是以内容的方式放在项目中的,我们也可以以嵌入的资源方式引入到项目中

这也是上面我将SwaggerUI文件夹删除,页面也能正常出来的原因。资源文件都被打包到dll中了,为了验证这个说法,使用反编译工具reflector。来反编译一下Swashbuckle.Core.dll

弄清楚了实现原理,现在来实现汉化。添加自己的中文语言包,和转换js,实现逻辑参考swagger源码。

  //c.InjectJavaScript(thisAssembly, "Swashbuckle.Dummy.SwaggerExtensions.testScript1.js");
  //路径规则,项目命名空间.文件夹名称.js文件名称c.InjectJavaScript(thisAssembly, "SwaggerDemo.Scripts.swaggerui.swagger_lang.js");

///    <summary>
/// 中文转换
///    </summary>
var SwaggerTranslator = (function () {//定时执行检测是否转换成中文,最多执行500次  即500*50/1000=25svar iexcute = 0,//中文语言包_words = {"Warning: Deprecated": "警告:已过时","Implementation Notes": "实现备注","Response Class": "响应类","Status": "状态","Parameters": "参数","Parameter": "参数","Value": "值","Description": "描述","Parameter Type": "参数类型","Data Type": "数据类型","Response Messages": "响应消息","HTTP Status Code": "HTTP状态码","Reason": "原因","Response Model": "响应模型","Request URL": "请求URL","Response Body": "响应体","Response Code": "响应码","Response Headers": "响应头","Hide Response": "隐藏响应","Headers": "头","Try it out!": "试一下!","Show/Hide": "显示/隐藏","List Operations": "显示操作","Expand Operations": "展开操作","Raw": "原始","can't parse JSON.  Raw result": "无法解析JSON. 原始结果","Model Schema": "模型架构","Model": "模型","apply": "应用","Username": "用户名","Password": "密码","Terms of service": "服务条款","Created by": "创建者","See more at": "查看更多:","Contact the developer": "联系开发者","api version": "api版本","Response Content Type": "响应Content Type","fetching resource": "正在获取资源","fetching resource list": "正在获取资源列表","Explore": "浏览","Show Swagger Petstore Example Apis": "显示 Swagger Petstore 示例 Apis","Can't read from server.  It may not have the appropriate access-control-origin settings.": "无法从服务器读取。可能没有正确设置access-control-origin。","Please specify the protocol for": "请指定协议:","Can't read swagger JSON from": "无法读取swagger JSON于","Finished Loading Resource Information. Rendering Swagger UI": "已加载资源信息。正在渲染Swagger UI","Unable to read api": "无法读取api","from path": "从路径","Click to set as parameter value": "点击设置参数","server returned": "服务器返回"},//定时执行转换_translator2Cn = function () {if ($("#resources_container .resource").length > 0) {_tryTranslate();}if ($("#explore").text() == "Explore" && iexcute < 500) {iexcute++;setTimeout(_translator2Cn, 50);}},//设置控制器注释_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 class="controller-summary" title="' + strSummary + '">' + strSummary + '</li>');}}});}});},//尝试将英文转换成中文_tryTranslate = function () {$('[data-sw-translate]').each(function () {$(this).html(_getLangDesc($(this).html()));$(this).val(_getLangDesc($(this).val()));$(this).attr('title', _getLangDesc($(this).attr('title')));});},_getLangDesc = function (word) {return _words[$.trim(word)] !== undefined ? _words[$.trim(word)] : word;};return {Translator: function () {document.title = "API描述文档";$('body').append('<style type="text/css">.controller-summary{color:#10a54a !important;word-break:keep-all;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:250px;text-align:right;cursor:default;} </style>');$("#logo").html("接口描述").attr("href", "/Home/Index");//设置控制器描述
            _setControllerSummary();_translator2Cn();         }}
})();
//执行转换
SwaggerTranslator.Translator();

2.控制器描述和接口文档缓存

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)){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", System.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;}}

View Code

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

上面汉化的js中的方法_setControllerSummary通过读取ControllerDesc属性设置了控制器的描述,至此项目可以无忧使用接口描述文档。

3.使用了MEF导致接口重复问题解决方案

代码请参照项目中的SwaggerConfig_解决MEF重复问题.cs文件

回到顶部

ApiExplorer思路拓展

该篇到这里可以结束了,考虑到有的读者想了解更多Swagger的实现内幕,这里再做一下简单的思路引导。

Swagger的读取所有Controller和Action借助于IApiExplorer接口的方法GetApiExplorer,其中IApiExplorerSystem.Web.Http中。

有兴趣的可以看一下ApiExplorer.cs源码,使用GlobalConfiguration.Configuration.Services.GetApiExplorer().ApiDescriptions 即可查看所有Api接口地址相关信息,Swagger正是借助于该方法导出所有接口信息,在结合xml文档添加相应注释文成接口描述文档的。

我们可以在Global.asax.cs  Application_Start中替换掉系统自带的ApiExploer服务,使用我们自己自定义的服务。

   public class CustomApiExplorer : ApiExplorer{public CustomApiExplorer(HttpConfiguration configuration) : base(configuration){}public override bool ShouldExploreAction(string actionVariableValue, HttpActionDescriptor actionDescriptor, IHttpRoute route){return base.ShouldExploreAction(actionVariableValue, actionDescriptor, route);}public override bool ShouldExploreController(string controllerVariableValue, HttpControllerDescriptor controllerDescriptor, IHttpRoute route){return base.ShouldExploreController(controllerVariableValue, controllerDescriptor, route);}}

 GlobalConfiguration.Configuration.Services.Replace(typeof(IApiExplorer), new CustomApiExplorer(GlobalConfiguration.Configuration)); 

接口有特有业务的可以考虑自定义ApiExplorer进行实现,或者在CachingSwaggerProvider中GetSwagge中进行实现。
回到顶部

总结

  有了这么方便的接口描述文档和接口测试工具,让前后端分离开发更加便于沟通和落地了,测试也可以不依赖于界面单独测试接口,有需要的可以使用起来。本篇所使用示例代码下载地址:SwaggerDemo,参考资源:

Swashbuckle:https://github.com/domaindrivendev/Swashbuckle

如果,您认为阅读这篇博客让您有些收获,不妨点击一下右下角的【推荐】按钮。
如果,您希望更容易地发现我的新博客,不妨点击一下绿。色通道的【关注我】。

如果,想给予我更多的鼓励,求打

因为,我的写作热情也离不开您的肯定支持。

感谢您的阅读,如果您对我的博客所讲述的内容有兴趣,请继续关注我的后续博客,我是焰尾迭 。

转载于:https://www.cnblogs.com/yanweidie/p/5709113.html

webapi文档描述-swagger相关推荐

  1. 接口文档神器Swagger(下篇)

    本文来自网易云社区 作者:李哲 二.Swagger-springmvc原理解析 上面介绍了如何将springmvc和springboot与swagger结合,通过简单配置生成接口文档,以及介绍了swa ...

  2. 接口文档神器Swagger(上篇)

    本文来自网易云社区 作者:李哲 接口文档管理一直是一个让人头疼的问题,伴随着各种接口文档管理平台涌现,如阿里开源的rap,ShowDoc,sosoapi,等等(网上能找到很多这种管理平台,包括我们自己 ...

  3. API文档工具-Swagger的集成

    最近安装了API文档工具swagger,因为Github上已有详细安装教程,且安装过程中没有碰到大的阻碍,所以此文仅对这次安装做一份大致记录 相关网站 Swagger 官方地址: http://swa ...

  4. Java:一个API文档框架Swagger

    新建SpringBoot项目,添加依赖 <!-- Swagger2 --> <dependency><groupId>io.springfox</groupI ...

  5. beego使用API自动化文档生成swagger时,routers目录下无法生成commentsRouter_controllers.go文件

    今天项目生成swagger时,按照官方文档操作,swagger目录和压缩包都正常创建,就连http://localhost:8080/swagger/也正常访问,但是 Try it out 接口时,就 ...

  6. webApi文档好帮手-apidoc使用教程

    在开发后台接口的过程中,我们肯定要提供一份api接口文档给终端app. 目前大多数的app的接口请求应该都是http+json的方式. 但是一直苦于做不出份漂亮的api文档,用word写,也太丑了.. ...

  7. SpringBoot工作日记(4)API文档框架 —— Swagger

    在使用了SpringBoot之后,我们不再像SpringMVC那样使用JSP了,通常使用Controller做接口,然后前端页面使用Ajax访问接口来交互数据,将前后端进行分离.这样约定接口格式就成了 ...

  8. spring-boot+swagger实现WebApi文档

    1.引用依赖包 <dependency><groupId>io.springfox</groupId><artifactId>springfox-swa ...

  9. CANoe-使用CANoe Demo、快速定位功能到Help文档描述

    1. 使用CANoe Demo 很多时候,我们想学习CANoe软件中的某个功能,或者CAPL中的某个函数.但是运行CANoe工程需要License的配合,而现在的License都是集成在CANoe硬件 ...

最新文章

  1. Glide-源码分析(一)
  2. MSD3458开发资料
  3. 每日一博 - 延时任务的多种实现方式解读
  4. acrobat xi pro 简体中文语言支持包_性能出众佳能PRO-561绘图仪西安优杰报价
  5. Polya原理的应用经典实例
  6. 对于《软件工程》课程的认识
  7. jeecg集成积木报表错误_6688种玩法的电子积木,是什么体验?「中外玩具网测评」...
  8. ScrollView嵌套StackView提示需要宽度和高度限制
  9. Ajax用POST方式传中文到SERVLET中,接收时乱码
  10. c 和java用cfb_一文彻底搞懂Java中的环境变量
  11. 群晖Docker部署Calibre Web打造全功能书库
  12. Centos7安装sqliteman
  13. ITIL学习(四) 服务、IT服务、服务台管理、IT服务管理
  14. 史上最全!作为一名Java面试者你应该知道的
  15. MAC微信小视频和图片等在本地保存位置地址
  16. C语言编写IDL动态可加载模块(DLM)入门
  17. PDF格式分析(一)简介
  18. 基于51单片机的水温水流量检测/智能水龙头控制系统proteus仿真原理图PCB
  19. 没有网络,也能上网-基于USSD技术的信息服务
  20. Collection集合入门

热门文章

  1. PHP多种序列化/反序列化的方法 (转载)
  2. CodeIgniter 2.X 于 PHP5.6 兼容错误
  3. 计算几何/sgu 124 Broken line
  4. C#配置及使用log4net
  5. 登录页面跳出框架的JS
  6. SQL -- 多表查询
  7. 1013 B. And
  8. Vue基础学习(一)------内部指令
  9. 『Python基础-12』各种推导式(列表推导式、字典推导式、集合推导式)
  10. Eclipse里修改SVN的用户名和密码