ASP.NET Web API 使用Swagger使用笔记

时间:2021-07-28 03:34:38

最近换了工作,其中Webapi这块没有文档,之前有了解过Swagger借此机会好好整理下常用的地方分享给有需要的小伙伴。

概述:

1.swagger 引用
2.swagger 问题1.action 方法名称相同处理
3.swagger 问题2.序列化出来的JSON NULL 值处理
4. 汉化及controller说明
5. 统一返回HttpResponseMessage 返回类型 指定
6. 自定义 HTTP Header (oauth2.0 请求)
7.请求示例remarks

 

1.swagger 引用

 第一步:

ASP.NET Web API 使用Swagger使用笔记

 

第二步:修改SwaggerConfig.cs

 如 api 版本号,title

ASP.NET Web API 使用Swagger使用笔记

 

第三步:创建项目XML注释文档

右键项目→属性→生成→选中下方的 "XML文档文件" 然后保存

ASP.NET Web API 使用Swagger使用笔记

配置启用:

c.IncludeXmlComments(string.Format("{0}/bin/BjxWebApis.XML",System.AppDomain.CurrentDomain.BaseDirectory));

第四步:启动项目

地址:http://localhost:58303/swagger

ASP.NET Web API 使用Swagger使用笔记

 

 哈哈 成功了,不对这个是最终效果,下面一步一步来实现吧。


2.swagger 问题1.action 方法名称相同处理

ASP.NET Web API 使用Swagger使用笔记

根据错误提示 很快发现 某位大神 同样的接口名 传递了不同参数,导致了这个错误,解决方式:

ASP.NET Web API 使用Swagger使用笔记

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

问题解决了 进行下一步


3.swagger 问题2.序列化出来的JSON NULL 值处理

先上图

ASP.NET Web API 使用Swagger使用笔记

等了好半天 一直不出来 打开F12一看原来有错,万能的网友帮了我,原来问题出在http://localhost:58303/swagger/docs/v1这个JSON资源上面,

序列化出来的JSON,包含了为NULL的字段,导致swagger-ui-min-js出现异常。

进一步分析是因为我项目使用的newtonsoft.json这个库的配置导致,应该忽略为NULL的字段,

对应解决办法如图: settings.NullValueHandling = NullValueHandling.Ignore;

ASP.NET Web API 使用Swagger使用笔记

 

问题解决了 开心 继续...


4. 汉化及controller说明

看图:咦 怎么控制器没有说明,这个和汉化一起说吧

ASP.NET Web API 使用Swagger使用笔记

 第一步:定义一个provider实现ISwaggerProvider接口 包含了缓存 名:SwaggerCacheProvider

代码:

   

 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 _xml;
 9         /// <summary>
10         /// 
11         /// </summary>
12         /// <param name="swaggerProvider"></param>
13         /// <param name="xml">xml文档路径</param>
14         public SwaggerCacheProvider(ISwaggerProvider swaggerProvider,string xml)
15         {
16             _swaggerProvider = swaggerProvider;
17             _xml = xml;
18         }
19 
20         public SwaggerDocument GetSwagger(string rootUrl, string apiVersion)
21         {
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 
30                 srcDoc.vendorExtensions = new Dictionary<string, object> { { "ControllerDesc", GetControllerDesc() } };
31                 _cache.TryAdd(cacheKey, srcDoc);
32             }
33             return srcDoc;
34         }
35 
36         /// <summary>
37         /// 从API文档中读取控制器描述
38         /// </summary>
39         /// <returns>所有控制器描述</returns>
40         public  ConcurrentDictionary<string, string> GetControllerDesc()
41         {
42             string xmlpath = _xml;
43             ConcurrentDictionary<string, string> controllerDescDict = new ConcurrentDictionary<string, string>();
44             if (File.Exists(xmlpath))
45             {
46                 XmlDocument xmldoc = new XmlDocument();
47                 xmldoc.Load(xmlpath);
48                 string type = string.Empty, path = string.Empty, controllerName = string.Empty;
49 
50                 string[] arrPath;
51                 int length = -1, cCount = "Controller".Length;
52                 XmlNode summaryNode = null;
53                 foreach (XmlNode node in xmldoc.SelectNodes("//member"))
54                 {
55                     type = node.Attributes["name"].Value;
56                     if (type.StartsWith("T:"))
57                     {
58                         //控制器
59                         arrPath = type.Split('.');
60                         length = arrPath.Length;
61                         controllerName = arrPath[length - 1];
62                         if (controllerName.EndsWith("Controller"))
63                         {
64                             //获取控制器注释
65                             summaryNode = node.SelectSingleNode("summary");
66                             string key = controllerName.Remove(controllerName.Length - cCount, cCount);
67                             if (summaryNode != null && !string.IsNullOrEmpty(summaryNode.InnerText) && !controllerDescDict.ContainsKey(key))
68                             {
69                                 controllerDescDict.TryAdd(key, summaryNode.InnerText.Trim());
70                             }
71                         }
72                     }
73                 }
74             }
75             return controllerDescDict;
76         }
77     }

第二步:定义一个JS文件,做成嵌入资源,这个js文件的功能主要有两个,一个是汉化,另一个就是在界面上显示控制器的描述文字

ASP.NET Web API 使用Swagger使用笔记

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

第三步:修改App_Start中的SwaggerConfig.cs文件,主要代码有两行

c.CustomProvider((defaultProvider) => new SwaggerCacheProvider(defaultProvider, string.Format("{0}/bin/BjxWebApis.XML", System.AppDomain.CurrentDomain.BaseDirectory)));

 c.InjectJavaScript(System.Reflection.Assembly.GetExecutingAssembly(), "BjxWebApis.swagger.js");

JS资源文件命名空间是:文件所在项目的命名空间.文件径路.文件名

 执行:

ASP.NET Web API 使用Swagger使用笔记

 

汉化有了 但是控制器说明没有,经过排查发现 var summaryDict = data.ControllerDesc; 没有获取到对象

使用var summaryDict = data.vendorExtensions.ControllerDesc;

再试,成功了,继续下一个目标,返回类型指定

ASP.NET Web API 使用Swagger使用笔记

 


5. 统一返回HttpResponseMessage 返回类型 指定

 很多时候我们会使用HttpResponseMessage  作为返回对象 很方便,但是Swagger 不知道我们具体返回啥,它不看我们的业务代码!!

直接上干货,使用SwaggerResponse 指定返回类型,两个httpstatuscode 对应不同返回值

ASP.NET Web API 使用Swagger使用笔记

看下效果

ASP.NET Web API 使用Swagger使用笔记

 

是不是想马上试试,可是问题又来了 接口有用户验证,没关系,继续看下一个

6. 自定义 HTTP Header (oauth2.0 请求)

在开发API时常常需要验证权限,验证参数放在Http请求头中是再好不过了。WebAPI配合过滤器验证权限即可

首先我们需要创建一个 IOperationFilter 接口的类。IOperationFilter:

上代码:

 1  /// <summary>
 2     /// swagger 增加 AUTH 选项
 3     /// </summary>
 4     public class HttpAuthHeaderFilter : IOperationFilter
 5     {
 6         /// <summary>
 7         /// 应用
 8         /// </summary>
 9         /// <param name="operation"></param>
10         /// <param name="schemaRegistry"></param>
11         /// <param name="apiDescription"></param>
12         public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription)
13 
14         {
15             if (operation.parameters == null)
16                 operation.parameters = new List<Parameter>();
17             var filterPipeline = apiDescription.ActionDescriptor.GetFilterPipeline(); //判断是否添加权限过滤器
18             var isAuthorized = filterPipeline.Select(filterInfo => filterInfo.Instance).Any(filter => filter is IAuthorizationFilter); //判断是否允许匿名方法 
19             var allowAnonymous = apiDescription.ActionDescriptor.GetCustomAttributes<AllowAnonymousAttribute>().Any();
20             if (isAuthorized && !allowAnonymous)
21             {
22                 operation.parameters.Add(new Parameter { name = "Authorization", @in = "header", description = "安全", required = false, type = "string" });
23             }
24         }
25     }

SwaggerConfig.cs 配置中加入  c.OperationFilter<HttpAuthHeaderFilter>();

ASP.NET Web API 使用Swagger使用笔记

看效果 可以 开始测试吧,可问题又来了 难道要对着实体对象编一个JSON对象,不用下一个我们来做个请求示例,继续...


7.请求示例remarks

先看个效果:

ASP.NET Web API 使用Swagger使用笔记

 

要想实现这个效果 ,我们需要使用呢remarks 看写法吧,需要说明的是 <remarks>前有个空格  请求地址 空格+tab 才能出来上面效果

/// <summary>
        /// 记录日志
        /// </summary>
        /// <remarks>
        /// 日志请求示例
        ///  
        ///     Post Api/Subject/Log
        ///  
        ///     {
        ///         "subjectId":100012,
        ///         "mouldId":0,
        ///         "statType":"10",
        ///         "entityId":0,
        ///         "viewUserId":1,
        ///         "ip":"127.0.0.1",
        ///         "devId":"1111",
        ///         "source":1
        ///     }
        /// </remarks>
        /// <param name="model"></param>
        /// <returns></returns>

 

总结:

规范化api的编写和注释,以及标准化文档,对于团队的开发效率有很大的提升,也有利于项目的维护。使用在线接口文档后,方便前后端工程师沟通,测试人员测试只需要在页面输入参数,点击调用就可以看到调用结果。

第一次写博客用了很长时间(将近3个小时)才写完,肯定会有很多不足,同时也深深觉得那些在园子里无私奉献的伙伴的辛苦,感谢他们!!!