ASP.NET Web API 使用Swagger使用筆記


最近換了工作,其中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 引用

 第一步:

 

第二步:修改SwaggerConfig.cs

 如 api 版本號,title

 

第三步:創建項目XML注釋文檔

右鍵項目→屬性→生成→選中下方的 "XML文檔文件" 然后保存

配置啟用:

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

第四步:啟動項目

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

 

 哈哈 成功了,不對這個是最終效果,下面一步一步來實現吧。


2.swagger 問題1.action 方法名稱相同處理

根據錯誤提示 很快發現 某位大神 同樣的接口名 傳遞了不同參數,導致了這個錯誤,解決方式:

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

問題解決了 進行下一步


3.swagger 問題2.序列化出來的JSON NULL 值處理

先上圖

等了好半天 一直不出來 打開F12一看原來有錯,萬能的網友幫了我,原來問題出在http://localhost:58303/swagger/docs/v1這個JSON資源上面,

序列化出來的JSON,包含了為NULL的字段,導致swagger-ui-min-js出現異常。

進一步分析是因為我項目使用的newtonsoft.json這個庫的配置導致,應該忽略為NULL的字段,

對應解決辦法如圖: settings.NullValueHandling = NullValueHandling.Ignore;

 

問題解決了 開心 繼續...


4. 漢化及controller說明

看圖:咦 怎么控制器沒有說明,這個和漢化一起說吧

 第一步:定義一個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文件的功能主要有兩個,一個是漢化,另一個就是在界面上顯示控制器的描述文字

  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資源文件命名空間是:文件所在項目的命名空間.文件徑路.文件名

 執行:

 

漢化有了 但是控制器說明沒有,經過排查發現 var summaryDict = data.ControllerDesc; 沒有獲取到對象

使用var summaryDict = data.vendorExtensions.ControllerDesc;

再試,成功了,繼續下一個目標,返回類型指定

 


5. 統一返回HttpResponseMessage 返回類型 指定

 很多時候我們會使用HttpResponseMessage  作為返回對象 很方便,但是Swagger 不知道我們具體返回啥,它不看我們的業務代碼!!

直接上干貨,使用SwaggerResponse 指定返回類型,兩個httpstatuscode 對應不同返回值

看下效果

 

是不是想馬上試試,可是問題又來了 接口有用戶驗證,沒關系,繼續看下一個

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>();

看效果 可以 開始測試吧,可問題又來了 難道要對着實體對象編一個JSON對象,不用下一個我們來做個請求示例,繼續...


7.請求示例remarks

先看個效果:

 

要想實現這個效果 ,我們需要使用呢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個小時)才寫完,肯定會有很多不足,同時也深深覺得那些在園子里無私奉獻的伙伴的辛苦,感謝他們!!!

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM