在ABP模板项目中,通过SwaggerUI可以为我们的WebApi生成动态的可交互接口文档页面,从而可以很方便的测试调用我们的WebApi接口。

一、集成Swagger

右键项目YoYo.Web,打开NuGet程序包管理器,添加Swashbuckle。

其中包含了程序和UI,安装后在App_Start文件夹下生成SwaggerConfig.cs。



完成这一步,Swagger已经集成完毕。

可以访问http://localhost:XXXX/swagger/ui/index。



如果出现异常,可以先尝试取消注释SwaggerConfig.cs中的此行代码

  1. c.ResolveConflictingActions(apiDescriptions => apiDescriptions.First());

二、增强配置

包括从XML接口文档读取,修改规则,汉化

swaggerconfig.cs修改如下:

  1. using System.Web.Http;
  2. using WebActivatorEx;
  3. using YoYo.Web;
  4. using Swashbuckle.Application;
  5. using System.Linq;
  6. using System.IO;
  7. [assembly: PreApplicationStartMethod(typeof(SwaggerConfig), "Register")]
  8. namespace YoYo.Web
  9. {
  10. public class SwaggerConfig
  11. {
  12. public static void Register()
  13. {
  14. var thisAssembly = typeof(SwaggerConfig).Assembly;
  15. GlobalConfiguration.Configuration
  16. .EnableSwagger(c =>
  17. {
  18. c.SingleApiVersion("v1", "YoYo");
  19. c.IncludeXmlComments(GetXmlCommentsPath("WebApi"));
  20. c.IncludeXmlComments(GetXmlCommentsPath("Application"));
  21. c.ResolveConflictingActions(apiDescriptions => apiDescriptions.First());
  22. c.CustomProvider((defaultProvider) => new CachingSwaggerProvider(defaultProvider));
  23. })
  24. .EnableSwaggerUi(c =>
  25. {
  26. string resourceName = thisAssembly.FullName.Substring(0, thisAssembly.FullName.IndexOf(",")) + ".Scripts.swaggerui.swagger_lang.js";
  27. c.InjectJavaScript(thisAssembly, resourceName);
  28. });
  29. }
  30. private static string GetXmlCommentsPath(string subName)
  31. {
  32. return Directory.GetFiles(System.AppDomain.CurrentDomain.BaseDirectory + "bin\\").FirstOrDefault(n => n.ToLower().EndsWith(subName.ToLower() + ".xml"));
  33. }
  34. }
  35. }

在App_Start下新增文件CachingSwaggerProvider.cs

  1. using Microsoft.Ajax.Utilities;
  2. using Swashbuckle.Swagger;
  3. using System;
  4. using System.Collections.Concurrent;
  5. using System.Collections.Generic;
  6. using System.IO;
  7. using System.Linq;
  8. using System.Web;
  9. using System.Xml;
  10. namespace YoYo.Web
  11. {
  12. public class CachingSwaggerProvider : ISwaggerProvider
  13. {
  14. private static ConcurrentDictionary<string, SwaggerDocument> _cache =
  15. new ConcurrentDictionary<string, SwaggerDocument>();
  16. private readonly ISwaggerProvider _swaggerProvider;
  17. public CachingSwaggerProvider(ISwaggerProvider swaggerProvider)
  18. {
  19. _swaggerProvider = swaggerProvider;
  20. }
  21. public SwaggerDocument GetSwagger(string rootUrl, string apiVersion)
  22. {
  23. var cacheKey = string.Format("{0}_{1}", rootUrl, apiVersion);
  24. SwaggerDocument srcDoc = null;
  25. //只读取一次
  26. if (!_cache.TryGetValue(cacheKey, out srcDoc))
  27. {
  28. srcDoc = _swaggerProvider.GetSwagger(rootUrl, apiVersion);
  29. srcDoc.paths.ForEach(n =>
  30. {
  31. SetOperation(n.Value.delete);
  32. SetOperation(n.Value.get);
  33. SetOperation(n.Value.head);
  34. SetOperation(n.Value.options);
  35. SetOperation(n.Value.patch);
  36. SetOperation(n.Value.post);
  37. SetOperation(n.Value.put);
  38. });
  39. srcDoc.vendorExtensions = new Dictionary<string, object> { { "ControllerDesc", GetControllerDesc() } };
  40. _cache.TryAdd(cacheKey, srcDoc);
  41. }
  42. return srcDoc;
  43. }
  44. private void SetOperation(Operation operation)
  45. {
  46. if (operation != null)
  47. {
  48. operation.tags[0] = operation.tags[0].Split('_').Last();
  49. if (operation.parameters == null)
  50. {
  51. operation.parameters = new List<Parameter>();
  52. }
  53. operation.parameters.Insert(0, new Parameter()
  54. {
  55. @in = "header",
  56. name = "authorization",
  57. description = "token",
  58. type = "string"
  59. });
  60. operation.parameters.Insert(0, new Parameter()
  61. {
  62. @in = "header",
  63. name = "X-XSRF-TOKEN",
  64. description = "XSRF-TOKEN",
  65. type = "string"
  66. });
  67. if (operation.operationId.StartsWith("Import"))
  68. {
  69. operation.parameters.Add(new Parameter()
  70. {
  71. @in = "formData",
  72. name = "",
  73. type = "file"
  74. });
  75. if (operation.consumes == null)
  76. {
  77. operation.consumes = new List<string>();
  78. }
  79. operation.consumes.Add("multipart/form-data");
  80. }
  81. }
  82. }
  83. /// <summary>
  84. /// 从API文档中读取控制器描述
  85. /// </summary>
  86. /// <returns>所有控制器描述</returns>
  87. public static ConcurrentDictionary<string, string> GetControllerDesc()
  88. {
  89. ConcurrentDictionary<string, string> controllerDescDict = new ConcurrentDictionary<string, string>();
  90. GetControllerDescByProject("WebApi", "Controller", controllerDescDict);
  91. GetControllerDescByProject("Application", "AppService", controllerDescDict);
  92. return controllerDescDict;
  93. }
  94. private static void GetControllerDescByProject(string subName, string endsWith, ConcurrentDictionary<string, string> controllerDescDict)
  95. {
  96. string xmlpath = Directory.GetFiles(System.AppDomain.CurrentDomain.BaseDirectory + "bin\\").FirstOrDefault(n => n.ToLower().EndsWith(subName.ToLower() + ".xml"));
  97. if (File.Exists(xmlpath))
  98. {
  99. XmlDocument xmldoc = new XmlDocument();
  100. xmldoc.Load(xmlpath);
  101. string type = string.Empty, path = string.Empty, controllerName = string.Empty;
  102. string[] arrPath;
  103. int length = -1, cCount = endsWith.Length;
  104. XmlNode summaryNode = null;
  105. foreach (XmlNode node in xmldoc.SelectNodes("//member"))
  106. {
  107. type = node.Attributes["name"].Value;
  108. if (type.StartsWith("T:"))
  109. {
  110. //控制器
  111. arrPath = type.Split('.');
  112. length = arrPath.Length;
  113. controllerName = arrPath[length - 1];
  114. if (controllerName.EndsWith(endsWith))
  115. {
  116. //获取控制器注释
  117. summaryNode = node.SelectSingleNode("summary");
  118. string key = controllerName.StartsWith("I") ?
  119. controllerName.Substring(1, 1).ToLower() + controllerName.Substring(2, controllerName.Length - 2 - cCount) :
  120. controllerName.Remove(controllerName.Length - cCount, cCount);
  121. if (summaryNode != null && !string.IsNullOrEmpty(summaryNode.InnerText) && !controllerDescDict.ContainsKey(key))
  122. {
  123. controllerDescDict.TryAdd(key, summaryNode.InnerText.Trim());
  124. }
  125. }
  126. }
  127. }
  128. }
  129. }
  130. }
  131. }

swagger_lang.js,需要将文件属性的“生成操作”设置为“嵌入的资源”,代码如下

  1. /// <summary>
  2. /// 中文转换
  3. /// </summary>
  4. var SwaggerTranslator = (function () {
  5. //定时执行检测是否转换成中文,最多执行500次 即500*50/1000=25s
  6. var iexcute = 0,
  7. //中文语言包
  8. _words = {
  9. "Warning: Deprecated": "警告:已过时",
  10. "Implementation Notes": "实现备注",
  11. "Response Class": "响应类",
  12. "Status": "状态",
  13. "Parameters": "参数",
  14. "Parameter": "参数",
  15. "Value": "值",
  16. "Description": "描述",
  17. "Parameter Type": "参数类型",
  18. "Data Type": "数据类型",
  19. "Response Messages": "响应消息",
  20. "HTTP Status Code": "HTTP状态码",
  21. "Reason": "原因",
  22. "Response Model": "响应模型",
  23. "Request URL": "请求URL",
  24. "Response Body": "响应体",
  25. "Response Code": "响应码",
  26. "Response Headers": "响应头",
  27. "Hide Response": "隐藏响应",
  28. "Headers": "头",
  29. "Try it out!": "试一下!",
  30. "Show/Hide": "显示/隐藏",
  31. "List Operations": "显示操作",
  32. "Expand Operations": "展开操作",
  33. "Raw": "原始",
  34. "can't parse JSON. Raw result": "无法解析JSON. 原始结果",
  35. "Model Schema": "模型架构",
  36. "Model": "模型",
  37. "apply": "应用",
  38. "Username": "用户名",
  39. "Password": "密码",
  40. "Terms of service": "服务条款",
  41. "Created by": "创建者",
  42. "See more at": "查看更多:",
  43. "Contact the developer": "联系开发者",
  44. "api version": "api版本",
  45. "Response Content Type": "响应Content Type",
  46. "fetching resource": "正在获取资源",
  47. "fetching resource list": "正在获取资源列表",
  48. "Explore": "浏览",
  49. "Show Swagger Petstore Example Apis": "显示 Swagger Petstore 示例 Apis",
  50. "Can't read from server. It may not have the appropriate access-control-origin settings.": "无法从服务器读取。可能没有正确设置access-control-origin。",
  51. "Please specify the protocol for": "请指定协议:",
  52. "Can't read swagger JSON from": "无法读取swagger JSON于",
  53. "Finished Loading Resource Information. Rendering Swagger UI": "已加载资源信息。正在渲染Swagger UI",
  54. "Unable to read api": "无法读取api",
  55. "from path": "从路径",
  56. "Click to set as parameter value": "点击设置参数",
  57. "server returned": "服务器返回"
  58. },
  59. //定时执行转换
  60. _translator2Cn = function () {
  61. if ($("#resources_container .resource").length > 0) {
  62. _tryTranslate();
  63. }
  64. if ($("#explore").text() === "Explore" && iexcute < 500) {
  65. iexcute++;
  66. setTimeout(_translator2Cn, 50);
  67. }
  68. },
  69. //设置控制器注释
  70. _setControllerSummary = function () {
  71. $.ajax({
  72. type: "get",
  73. async: true,
  74. url: $("#input_baseUrl").val(),
  75. dataType: "json",
  76. success: function (data) {
  77. var summaryDict = data.ControllerDesc;
  78. var id, controllerName, strSummary;
  79. $("#resources_container .resource").each(function (i, item) {
  80. id = $(item).attr("id");
  81. if (id) {
  82. controllerName = id.substring(9);
  83. strSummary = summaryDict[controllerName];
  84. if (strSummary) {
  85. $(item).children(".heading").children(".options").prepend('<li class="controller-summary" title="' + strSummary + '">' + strSummary + '</li>');
  86. }
  87. }
  88. });
  89. }
  90. });
  91. },
  92. //尝试将英文转换成中文
  93. _tryTranslate = function () {
  94. $('[data-sw-translate]').each(function () {
  95. $(this).html(_getLangDesc($(this).html()));
  96. $(this).val(_getLangDesc($(this).val()));
  97. $(this).attr('title', _getLangDesc($(this).attr('title')));
  98. });
  99. },
  100. _getLangDesc = function (word) {
  101. return _words[$.trim(word)] !== undefined ? _words[$.trim(word)] : word;
  102. };
  103. return {
  104. Translator: function () {
  105. document.title = "API描述文档";
  106. $('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>');
  107. $("#logo").html("接口描述").attr("href", "/Home/Index");
  108. //设置控制器描述
  109. _setControllerSummary();
  110. _translator2Cn();
  111. }
  112. }
  113. })();
  114. //执行转换
  115. SwaggerTranslator.Translator();
  116. function setCookie(c_name, value, expiredays) {
  117. var exdate = new Date()
  118. exdate.setDate(exdate.getDate() + expiredays)
  119. document.cookie = c_name + "=" + escape(value) +
  120. ((expiredays == null) ? "" : ";expires=" + exdate.toGMTString())
  121. }
  122. function getCookie(c_name) {
  123. if (document.cookie.length > 0) {
  124. c_start = document.cookie.indexOf(c_name + "=")
  125. if (c_start != -1) {
  126. c_start = c_start + c_name.length + 1
  127. c_end = document.cookie.indexOf(";", c_start)
  128. if (c_end == -1) c_end = document.cookie.length
  129. return unescape(document.cookie.substring(c_start, c_end))
  130. }
  131. }
  132. return ""
  133. }
  134. var token = getCookie("xToken");
  135. if (token) {
  136. $('[name="authorization"]').val(token);
  137. }
  138. var XSRFToken = getCookie("XSRF-TOKEN");
  139. if (XSRFToken) {
  140. $('[name="X-XSRF-TOKEN"]').val(XSRFToken);
  141. }
  142. var setToken = function () {
  143. var json = $("#resource_Account .response_body.json").text()
  144. if (json) {
  145. var obj = eval('(' + json + ')');
  146. if (obj.result) {
  147. $('[name="authorization"]').val("Bearer " + obj.result);
  148. setCookie("xToken", "Bearer " + obj.result);
  149. }
  150. }
  151. }
  152. setInterval(setToken, 5000)

另外,在Application和WebApi的项目属性,生成输出中,勾选“XML文档文件”,如下图

三、最终效果

代码中还为接口自动添加了X-XSRF-TOKEN和authorization两个参数,可以让我们更方便的调用加了权限的接口。

ABP给WebApi添加SwaggerUI,生成可交互接口文档的更多相关文章

  1. Asp.Net Core2.0 WebAPI 使用Swagger生成漂亮的接口文档

    1.引用NuGet: Swashbuckle.AspNetCore.Swagger Swashbuckle.AspNetCore.SwaggerGen 或 <PackageReference I ...

  2. 整合swagger2生成Restful Api接口文档

    整合swagger2生成Restful Api接口文档 swagger Restful文档生成工具 2017-9-30 官方地址:https://swagger.io/docs/specificati ...

  3. Swagger 生成 PHP API 接口文档

    Swagger 生成 PHP API 接口文档 Lumen微服务生成Swagger文档 1.概况 有同学反馈写几十个接口文档需要两天的工作量, 随着多部门之间的协作越来越频繁, 维护成本越来越高, 文 ...

  4. 如何快速方便的生成好看的接口文档(apipost生成文档)

    一键生成文档 我们在"2分钟玩转APIPOST"一讲中,简单介绍了如何生成并分享接口文档: 点击分享文档 复制并打开文档地址就可以看到了完整的接口文档. 本节课主要是讲解一些需要注 ...

  5. 如何使用Swagger-UI在线生成漂亮的接口文档

    一.简单介绍 Swagger是一个实现了OpenAPI(OpenAPI Specification)规范的工具集.OpenAPI是Linux基金会的一个项目,试图通过定义一种用来描述API格式或API ...

  6. Abp中SwaggerUI的多个接口文档配置说明

    对外提供的接口在实际生成过程中,可能是需要一个接口版本的,比如说v1,manage.效果如下:     在swagger中怎么实现呢? 1. 添加SwaggerVersionHelper.cs pub ...

  7. SpringBoot结合swagger2快速生成简单的接口文档

    1. pom.xml中加入依赖 <dependency> <groupId>com.spring4all</groupId> <artifactId>s ...

  8. 自动生成web api接口文档

    然后打开web程序,访问ip:port/Help. 为什么可以直接输入Help就能访问呢,因为这个插件本身已经配置了路径,如下. public class HelpPageAreaRegistrati ...

  9. Java | Spring Boot Swagger2 集成REST ful API 生成接口文档

      Spring Boot Swagger2 集成REST ful API 生成接口文档 原文 简介 由于Spring Boot 的特性,用来开发 REST ful 变得非常容易,并且结合 Swagg ...

随机推荐

  1. 四、Java多人博客系统-2.0版本

    由于时间关系,多人博客系统这里穿插一个2.0版本. 2.0版本本来是打算用于建立个人网站,但是后来发现个人建站需要购买域名服务器,还需要备案,很繁琐.最终放弃.完成此版本,最终也只是作为技术演练.此版 ...

  2. apache beam ElasticSearchIO 遇到异常后job中断执行 自己定制beam IO

    可以将 ElasticSearchIO.java 源码拷贝至自己的项目.改名为MyElasticSearchIO.java.自己定制实现.这时编译会遇到auto.value失败.在pom.xml中加入 ...

  3. FreeNAS:创建 CIFS 匿名共享

    第一部分:数据集权限设定 浏览器访问 FreeNAS 系统的 WebGUI 管理界面,点选 “Storage” 存储图标打开存储选项卡,在卷列表中点选用于匿名共享的数据集,如有需要,也可以自行创建新的 ...

  4. (二叉树 递归) leetcode 106. Construct Binary Tree from Inorder and Postorder Traversal

    Given inorder and postorder traversal of a tree, construct the binary tree. Note:You may assume that ...

  5. 【强大精美的PS特效滤镜合集】Alien Skin Eye Candy for Mac 7.2.2.20

    [简介] Alien Skin Eye Candy for Mac 7.2.2 版本,支持最新的PhotoShop CC 2019.2018等版本,这是一款强大酷炫的PS特效滤镜合集,具有32种滤镜和 ...

  6. Python动态语言的特性

    一.动态语言相关概念 1.1 动态语言 在运行时代码可以根据某些条件改变自身结构 可以在运行时引进新的函数.对象.甚至代码,可以删除已有的函数等其他结构上的变化 常见的动态语言:Object-C.C# ...

  7. Web请求相关

    HTTP简介 HTTP协议是Hyper Text Transfer Protocol(超文本传输协议)的缩写,是用于从万维网(WWW:World Wide Web )服务器传输超文本到本地浏览器的传送 ...

  8. 1.5分布式通讯框架-RMI

    分布式通信框架-RMI讲解 什么是RPC Remote procedure call protocal RPC协议其实是一个规范.常用PRC框架:Dubbo.Thrif.RMI.Webservice. ...

  9. Hadoop记录- Yarn scheduler队列采集

    #!/bin/sh ip=10.116.100.11 port=8088 export HADOOP_HOME=/app/hadoop/bin rmstate1=$($HADOOP_HOME/yarn ...

  10. Contest2158 - 2019-3-14 高一noip基础知识点 测试3 题解版

    传送门 预计得分:0 实际得分:90 还行 T1 数学卡精 二分double卡精 反正就是卡精 怎么办?卡回去!! 将double*=1e4,变成一个long long 注意四舍五入的奇技淫巧 代码 ...