@

开发 web api 的时候,写文档是个痛苦的事情,而没有文档别人就不知道怎么调用,所以又不得不写。

swagger 可以自动生成接口文档,并测试接口,极大的解放了程序员的生产力。

1 安装

通过 NuGet 安装 Swashbuckle。



安装完成后,App_Start 文件夹下会多出一个 SwaggerConfig.cs 文件。



重新生成并发布 api,打开网页 http://localhost:7001/swagger(这里注意换成你的 host)

网页显示如下:

2 修改名称和版本号

上图中框出的名称和版本号是可以修改的,打开 SwaggerConfig.cs 文件,找到如下代码:

  1. c.SingleApiVersion("v1", "API.Test");

修改其中的参数,重新发布即可。

3 显示说明

swagger 可以读取代码中的注释,并显示在网页上。如此一来,我们只需要在代码中将注释写好,就可以生成一份可供他人阅读的 API 文档了。

swagger 是通过编译时生成的 xml 文件来读取注释的。这个 xml 文件默认是不生成的,所以先要修改配置。

第一步: 右键项目 -> 属性 -> 生成,把 XML 文档文件勾上。



第二步: 添加配置

在 SwaggerConfig.cs 文件中添加配置如下:

  1. GlobalConfiguration.Configuration
  2. .EnableSwagger(c =>
  3. {
  4. c.SingleApiVersion("v2", "测试 API 接口文档");
  5. // 配置 xml 文件路径
  6. c.IncludeXmlComments($@"{System.AppDomain.CurrentDomain.BaseDirectory}\bin\API.Test.xml");
  7. })

注意:发布的时候,XML 文件不会一起发布,需要手动拷到发布目录下。

4 显示控制器注释及汉化

默认是不会显示控制器注释的,需要自己写。

在 App_Start 中新建类 SwaggerControllerDescProvider,代码如下:

  1. /// <summary>
  2. /// swagger 显示控制器的描述
  3. /// </summary>
  4. public class SwaggerCacheProvider : ISwaggerProvider
  5. {
  6. private readonly ISwaggerProvider _swaggerProvider;
  7. private static ConcurrentDictionary<string, SwaggerDocument> _cache = new ConcurrentDictionary<string, SwaggerDocument>();
  8. private readonly string _xmlPath;
  9. /// <summary>
  10. ///
  11. /// </summary>
  12. /// <param name="swaggerProvider"></param>
  13. /// <param name="xmlpath">xml文档路径</param>
  14. public SwaggerCacheProvider(ISwaggerProvider swaggerProvider, string xmlpath)
  15. {
  16. _swaggerProvider = swaggerProvider;
  17. _xmlPath = xmlpath;
  18. }
  19. public SwaggerDocument GetSwagger(string rootUrl, string apiVersion)
  20. {
  21. var cacheKey = string.Format("{0}_{1}", rootUrl, apiVersion);
  22. //只读取一次
  23. if (!_cache.TryGetValue(cacheKey, out SwaggerDocument srcDoc))
  24. {
  25. srcDoc = _swaggerProvider.GetSwagger(rootUrl, apiVersion);
  26. srcDoc.vendorExtensions = new Dictionary<string, object>
  27. {
  28. { "ControllerDesc", GetControllerDesc() }
  29. };
  30. _cache.TryAdd(cacheKey, srcDoc);
  31. }
  32. return srcDoc;
  33. }
  34. /// <summary>
  35. /// 从API文档中读取控制器描述
  36. /// </summary>
  37. /// <returns>所有控制器描述</returns>
  38. public ConcurrentDictionary<string, string> GetControllerDesc()
  39. {
  40. ConcurrentDictionary<string, string> controllerDescDict = new ConcurrentDictionary<string, string>();
  41. if (File.Exists(_xmlPath))
  42. {
  43. XmlDocument xmldoc = new XmlDocument();
  44. xmldoc.Load(_xmlPath);
  45. string[] arrPath;
  46. int cCount = "Controller".Length;
  47. foreach (XmlNode node in xmldoc.SelectNodes("//member"))
  48. {
  49. string type = node.Attributes["name"].Value;
  50. if (type.StartsWith("T:"))
  51. {
  52. arrPath = type.Split('.');
  53. string controllerName = arrPath[arrPath.Length - 1];
  54. if (controllerName.EndsWith("Controller")) //控制器
  55. {
  56. //获取控制器注释
  57. XmlNode summaryNode = node.SelectSingleNode("summary");
  58. string key = controllerName.Remove(controllerName.Length - cCount, cCount);
  59. if (summaryNode != null && !string.IsNullOrEmpty(summaryNode.InnerText) && !controllerDescDict.ContainsKey(key))
  60. {
  61. controllerDescDict.TryAdd(key, summaryNode.InnerText.Trim());
  62. }
  63. }
  64. }
  65. }
  66. }
  67. return controllerDescDict;
  68. }
  69. }

另外,新建 swagger.js 文件并将其设置成 嵌入的资源,这个文件的作用就是显示控制器注释及汉化。js 代码如下:

  1. 'use strict';
  2. window.SwaggerTranslator = {
  3. _words: [],
  4. translate: function () {
  5. var $this = this;
  6. $('[data-sw-translate]').each(function () {
  7. $(this).html($this._tryTranslate($(this).html()));
  8. $(this).val($this._tryTranslate($(this).val()));
  9. $(this).attr('title', $this._tryTranslate($(this).attr('title')));
  10. });
  11. },
  12. setControllerSummary: function () {
  13. $.ajax({
  14. type: "get",
  15. async: true,
  16. url: $("#input_baseUrl").val(),
  17. dataType: "json",
  18. success: function (data) {
  19. var summaryDict = data.ControllerDesc;
  20. var id, controllerName, strSummary;
  21. $("#resources_container .resource").each(function (i, item) {
  22. id = $(item).attr("id");
  23. if (id) {
  24. controllerName = id.substring(9);
  25. strSummary = summaryDict[controllerName];
  26. if (strSummary) {
  27. $(item).children(".heading").children(".options").first().prepend('<li class="controller-summary" title="' + strSummary + '">' + strSummary + '</li>');
  28. }
  29. }
  30. });
  31. }
  32. });
  33. },
  34. _tryTranslate: function (word) {
  35. return this._words[$.trim(word)] !== undefined ? this._words[$.trim(word)] : word;
  36. },
  37. learn: function (wordsMap) {
  38. this._words = wordsMap;
  39. }
  40. };
  41. /* jshint quotmark: double */
  42. window.SwaggerTranslator.learn({
  43. "Warning: Deprecated": "警告:已过时",
  44. "Implementation Notes": "实现备注",
  45. "Response Class": "响应类",
  46. "Status": "状态",
  47. "Parameters": "参数",
  48. "Parameter": "参数",
  49. "Value": "值",
  50. "Description": "描述",
  51. "Parameter Type": "参数类型",
  52. "Data Type": "数据类型",
  53. "Response Messages": "响应消息",
  54. "HTTP Status Code": "HTTP 状态码",
  55. "Reason": "原因",
  56. "Response Model": "响应模型",
  57. "Request URL": "请求 URL",
  58. "Response Body": "响应体",
  59. "Response Code": "响应码",
  60. "Response Headers": "响应头",
  61. "Hide Response": "隐藏响应",
  62. "Headers": "头",
  63. "Try it out!": "试一下!",
  64. "Show/Hide": "显示/隐藏",
  65. "List Operations": "显示操作",
  66. "Expand Operations": "展开操作",
  67. "Raw": "原始",
  68. "can't parse JSON. Raw result": "无法解析 JSON。原始结果",
  69. "Model Schema": "模型架构",
  70. "Model": "模型",
  71. "apply": "应用",
  72. "Username": "用户名",
  73. "Password": "密码",
  74. "Terms of service": "服务条款",
  75. "Created by": "创建者",
  76. "See more at": "查看更多:",
  77. "Contact the developer": "联系开发者",
  78. "api version": "api 版本",
  79. "Response Content Type": "响应 Content Type",
  80. "fetching resource": "正在获取资源",
  81. "fetching resource list": "正在获取资源列表",
  82. "Explore": "浏览",
  83. "Show Swagger Petstore Example Apis": "显示 Swagger Petstore 示例 Apis",
  84. "Can't read from server. It may not have the appropriate access-control-origin settings.": "无法从服务器读取。可能没有正确设置 access-control-origin。",
  85. "Please specify the protocol for": "请指定协议:",
  86. "Can't read swagger JSON from": "无法读取 swagger JSON于",
  87. "Finished Loading Resource Information. Rendering Swagger UI": "已加载资源信息。正在渲染 Swagger UI",
  88. "Unable to read api": "无法读取 api",
  89. "from path": "从路径",
  90. "server returned": "服务器返回"
  91. });
  92. $(function () {
  93. window.SwaggerTranslator.translate();
  94. window.SwaggerTranslator.setControllerSummary();
  95. });

在 SwaggerConfig.cs 添加如下配置:

  1. GlobalConfiguration.Configuration
  2. .EnableSwagger(c =>
  3. {
  4. c.CustomProvider((defaultProvider) => new SwaggerCacheProvider(defaultProvider, $@"{System.AppDomain.CurrentDomain.BaseDirectory}\bin\API.Test.xml"));
  5. })
  6. .EnableSwaggerUi(c =>
  7. {
  8. c.InjectJavaScript(System.Reflection.Assembly.GetExecutingAssembly(), "API.Test.swagger.js");
  9. });

5 路由相同,查询参数不同的方法

在实际的 ASP.NET Web API 中,是可以存在 路由相同HTTP 方法相同查询参数不同 的方法的,但不好意思,swagger 中不支持,并且会直接报错。

如下代码,

  1. [Route("api/users")]
  2. public IEnumerable<User> Get()
  3. {
  4. return users;
  5. }
  6. [Route("api/users")]
  7. public IEnumerable<User> Get(int sex)
  8. {
  9. return users;
  10. }

报错: Multiple operations with path 'api/users' and method 'GET'.



可以在 SwaggerConfig.cs 添加如下配置:

  1. GlobalConfiguration.Configuration
  2. .EnableSwagger(c =>
  3. {
  4. c.ResolveConflictingActions(apiDescriptions => apiDescriptions.First());
  5. })

此配置的意思是,当遇到此种情况时,取第一个方法展示。

这可以避免报错,但多个方法只会在 swagger 中展示一个。治标不治本,不推荐。所以唯一的解决方案就是设置成不同的路由。不知道这个问题在之后的版本中会不会修复。

6 忽略 Model 中的某些字段

如下图,新建用户时,后台需要一个 User 类作为参数。点击右侧的 Model,可以显示 User 类的属性及注释。



但是,有些字段其实是无需调用者传递的。例如 State,调用者无需知道这些字段的存在。

给这些属性标记上 [Newtonsoft.Json.JsonIgnore] 特性,swagger 中不再显示了。

当然这种做法也是有缺点的,因为 web api 在返回数据时,调用的默认序列化方法也是 Newtonsoft.Json 序列化。

7 传递 header

调用 api 时,有些信息是放在 HTTP Header 中的,例如 token。这个 swagger 也是支持的。

新建一个特性:

  1. public class ApiAuthorizeAttribute : Attribute
  2. {
  3. }

新建一个类代码如下:

  1. public class AuthorityHttpHeaderFilter : IOperationFilter
  2. {
  3. public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription)
  4. {
  5. if (operation.parameters == null)
  6. operation.parameters = new List<Parameter>();
  7. //判断是否添加权限过滤器
  8. var isAuthorized = apiDescription.ActionDescriptor.GetCustomAttributes<ApiAuthorizeAttribute>().Any();
  9. if (isAuthorized)
  10. {
  11. operation.parameters.Add(new Parameter { name = "token", @in = "header", description = "令牌", required = false, type = "string" });
  12. }
  13. }
  14. }

这段代码就是告诉 swagger,如果遇到的方法上标记了 ApiAuthorizeAttribute 特性,则添加一个名为 token 的参数在 header 中。

最后需要在 SwaggerConfig.cs 中注册这个过滤器。

  1. GlobalConfiguration.Configuration
  2. .EnableSwagger(c =>
  3. {
  4. c.OperationFilter<AuthorityHttpHeaderFilter>();
  5. })

效果如下:

8 出错时的 HTTP 状态码

我们在方法中返回一个 400

  1. [Route("api/users")]
  2. public HttpResponseMessage Post([FromBody]User user)
  3. {
  4. return new HttpResponseMessage()
  5. {
  6. Content = new StringContent("新建用户出错", Encoding.UTF8, "application/json"),
  7. StatusCode = HttpStatusCode.BadRequest
  8. };
  9. }

可是,swagger 中返回的状态码却是 0。



这是因为 Content 指定了 JSON 格式,但传入的 content 又不是 JSON 格式的。

content 改为 JSON 格式,或者将 mediaType 改成 text/plain 就可以了。

【swagger】C# 中 swagger 的使用及避坑的更多相关文章

  1. ASP.NET Core 在 Swagger UI 中显示自定义的 Header Token

    Swagger 是个好东西,对于前后端分离的网站来说,不仅是提高前后端开发人员沟通效率的利器,也大大方便了后端人员测试 API.有时候,API 中可能需要在 Header 中设置认证参数,比如 aut ...

  2. 新版本 swagger 组件中 Servers 的 坑

    新版本 Swashbuckle swagger 组件中 Servers 的 坑 Intro 上周做了公司的项目升级,从 2.2 更新到 3.1, swagger 直接更新到了最新,swagger 用的 ...

  3. Swagger UI及 Swagger editor教程 API文档搭配 Node使用

    swagger ui 是一个在线文档生成和测试的利器,目前发现最好用的.为啥好用呢?打开 demo,支持API自动生成同步的在线文档些文档可用于项目内部API审核方便测试人员了解 API这些文档可作为 ...

  4. swagger.yaml转换为swagger.json文件

    方法一 swagger-editor页面 官方的 swagger-editor Live Demo (在线直接使用,就是访问的有点慢)或者将swagger-editor Download 下载到本地然 ...

  5. 在mpvue中使用map如何避坑

    最近在做一个需求,当用户放大地图到某个级别时,自动显示marker的callout标签,当小于这个缩放级别时,则隐藏callout.然而在我实现的过程中,却发现一个严重的问题:当我操作marker数据 ...

  6. swagger core 和 swagger ui 如何关联【窥探】

    几个片段: package io.swagger.jaxrs.listing; import io.swagger.annotations.ApiOperation; import org.apach ...

  7. 珍爱生命,远离JS=>JS避坑记

    JavaScript避坑记 转载请注明源地址: http://www.cnblogs.com/funnyzpc/p/8407952.html 上图=> 有意思的漫画,不知大家看懂了没,这里我想说 ...

  8. 双刃剑MongoDB的学习和避坑

    双刃剑MongoDB的学习和避坑 MongoDB 是一把双刃剑,它对数据结构的要求并不高.数据通过key-value的形式存储,而value的值可以是字符串,也可以是文档.所以我们在使用的过程中非常方 ...

  9. Windows环境下Anaconda安装TensorFlow的避坑指南

    最近群里聊天时经常会提到DL的东西,也有群友在学习mxnet,但听说坑比较多.为了赶上潮流顺便避坑,我果断选择了TensorFlow,然而谁知一上来就掉坑里了…… 我根据网上的安装教程,默认安装了最新 ...

  10. spring-boot-starter-thymeleaf 避坑指南

    第一步:pom配置环境 先不要管包是做什么的 总之必须要有 否则进坑 <!--避坑包--> <dependency> <groupId>net.sourceforg ...

随机推荐

  1. 授人以渔式解析原生JS写轮播图

    需求与分析 需求:循环无缝自动轮播五张图,按左右箭头可以手动切换图片,鼠标点击轮播图下面按钮 1 2 3 4 5会跳转到对应的第1 2 3 4 5张图片.鼠标放到轮播图的图片上时不再自动轮播并且左右箭 ...

  2. 关于Sprites的一些理解

    今天做测试,遇到一道选择题. 瞬间一脸懵逼,sprites是什么?通过对各选项的分析,大致明白了几点:1.它是css属性.2.它与图片有关.3.它是背景图片.然后就选了一个大概不靠谱的,成功的选错了. ...

  3. TCP/IP协议概要--01

    学习一下tcp/ip协议,还是很枯燥,哎..... 图片的是从下到上对TCP/IP的协议进行描述的.主要是的描述每一层协议的特点 该层对应的是最底层的数据链路层,即图中的以太网驱动程序那一层. 该层是 ...

  4. ubuntu下安装typescript

    安装ts之前需要安装好node, 安装ts: 1. npm install -g typescript /opt/node/bin/tsc -> /opt/node/lib/node_modul ...

  5. 关于独立部署web项目到tomcat服务器详情

    步骤: 1.设置端口号:找到所解压的tomcat的目录下中的conf文件夹,再用editPlus打开conf文件夹中的server.xml文件,tomcat初始端口为8005,8080,8009,如果 ...

  6. 0312 java接口测试三棱军刺rest-assured

    背景 java程序员一般写的是后端服务是JavaWeb类型的项目,主要包括Http接口和dubbo接口,Http接口一般采用的rest风格,那么如何快速的对rest接口在第三方的测试框架上进行测试呢? ...

  7. ajax jsonP 解决跨域问题

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  8. 谈谈MySQL数据库索引

    在分析MySQL数据库索引之前,很多小伙伴对数据结构中的树理解不够深刻.因此我们由浅入深一步步探讨树的演进过程,再一步步引出MySQL数据库索引底层数据结构. 一.二叉树 二叉查找树也称为有序二叉查找 ...

  9. Java并发编程之验证volatile不能保证原子性

    Java并发编程之验证volatile不能保证原子性 通过系列文章的学习,凯哥已经介绍了volatile的三大特性.1:保证可见性 2:不保证原子性 3:保证顺序.那么怎么来验证可见性呢?本文凯哥(凯 ...

  10. linux环境下安装可操作图库语言Gremlin的图框架HugeGraph

    原创/朱季谦 图数据库是一项比较前沿而逐渐热门的技术,是NoSql数据库的一种,它应用图形理论存储实体之间的关系信息,最主要的组成有两种,结点集和连接结点的边.常见的图数据库有Neo4j,Januas ...