Swagger學習筆記


什么是Swagger

官網地址:https://swagger.io/docs/specification/about/

Swagger是一套圍繞OpenAPI規范構建的開源工具,可以幫助您設計,構建,記錄和使用REST API。主要的Swagger工具包括:

Swagger Editor - 基於瀏覽器的編輯器,您可以在其中編寫OpenAPI規范。

Swagger UI - 將OpenAPI規范呈現為交互式API文檔。使用戶可以直接在瀏覽器中嘗試API調用。

Swagger Codegen - 根據OpenAPI規范生成服務器存根和客戶端庫,支持超過40種語言為您的API 生成客戶端庫。

什么是OpenAPI?

OpenAPI規范(以前稱為Swagger規范)是REST API的API描述格式。OpenAPI文件允許您描述整個API,包括:  

·每個端點上的可用端點(/users)和操作(GET /users,POST /users)

·操作參數每個操作的輸入和輸出

·驗證方法

·聯系信息,許可,使用條款和其他信息。

·API規范可以用YAML或JSON編寫。該格式易於學習,並且對人類和機器都可讀。完整的OpenAPI規范可以在GitHub上找到:OpenAPI 3.0規范

一、Swagger生成在線接口文檔

創建一個WebApi項目,項目名Swagger,如圖:

 

通過Nuget安裝Swashbuckle,會自動生成一個Swagger配置類SwaggerConfig.cs

 

右鍵項目->屬性->生成 勾選XML文檔文件

 

在SwaggerConfig類的EnableSwagger方法中配置xml文檔文件地址,加入以下代碼:

c.IncludeXmlComments($"{AppDomain.CurrentDomain.BaseDirectory}/bin/Swagger.xml");

此句代碼包括下面提到要加入的代碼在SwaggerConfig類中都有,只是都被注釋了。

運行項目,成功顯示Swagger UI界面,如下圖:

 

二、漢化

通過Swagger生成的在線接口文檔功能顯示的是英文,通過js實現漢化。在網上找了一個漢化腳本swagger.js,代碼如下:

'use strict';

window.SwaggerTranslator = {

    _words: [],

 

    translate: function () {

        var $this = this;

        $('[data-sw-translate]').each(function () {

            $(this).html($this._tryTranslate($(this).html()));

            $(this).val($this._tryTranslate($(this).val()));

            $(this).attr('title', $this._tryTranslate($(this).attr('title')));

        });

    },

 

    setControllerSummary: function () {

 

        try {

            console.log($("#input_baseUrl").val());

            $.ajax({

                type: "get",

                async: true,

                url: $("#input_baseUrl").val(),

                dataType: "json",

                success: function (data) {

 

                    var summaryDict = data.ControllerDesc;

                    console.log(summaryDict);

                    var id, controllerName, strSummary;

                    $("#resources_container .resource").each(function (i, item) {

                        id = $(item).attr("id");

                        if (id) {

                            controllerName = id.substring(9);

                            try {

                                strSummary = summaryDict[controllerName];

                                if (strSummary) {

                                    $(item).children(".heading").children(".options").first().prepend('<li class="controller-summary" style="color:green;" title="' + strSummary + '">' + strSummary + '</li>');

                                }

                            } catch (e) {

                                console.log(e);

                            }

                        }

                    });

                }

            });

        } catch (e) {

            console.log(e);

        }

    },

    _tryTranslate: function (word) {

        return this._words[$.trim(word)] !== undefined ? this._words[$.trim(word)] : word;

    },

 

    learn: function (wordsMap) {

        this._words = wordsMap;

    }

};

 

window.SwaggerTranslator.learn({

    "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": "從路徑",

    "server returned": "服務器返回"

});

$(function () {

    window.SwaggerTranslator.translate();

    window.SwaggerTranslator.setControllerSummary();

});

我把swagger.js放在了根目錄下,swagger.js必須修改成嵌入的資源,如下圖:

 

最后在SwaggerConfig類的EnableSwaggerUi方法中加入以下代碼:

c.InjectJavaScript(thisAssembly, "Swagger.swagger.js");

運行項目,漢化成功,如下圖:

 

三、相同操作出現多個方法報錯

此時如果再寫一個帶參Get方法,運行項目會報錯,提示api/Values下有多個Get方法,如下圖:

 

在SwaggerConfig類的方法EnableSwagger中加入以下代碼可以解決報錯問題:

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

運行項目,報錯解決了,但是因為上面的代碼是只取第一個方法,所以新寫的方法不會出現在Swagger UI界面中,如下圖:

 

為了顯示出新的Get方法,我修改了路由規則,路由中增加了方法名,如下圖:

 

重新運行項目,如下圖:

 

如果再寫一個Get操作參數不一樣的GetTest方法,這個方法也會被過濾掉,不會顯示在Swagger UI界面。但是如果改成Post操作,是可以顯示的,所以在Api接口方法中相同類型的操作要避免方法重名。如下圖:

 

四、Token

Api接口肯定會用到Token驗證,一般是把Token值放在請求頭Header里,在請求參數里增加Token選項,在網上找了一段代碼,類名我命名為HeaderTokenFilter ,如下:

    /// <summary>

    /// swagger 參數增加TOKEN選項

    /// </summary>

    public class HeaderTokenFilter : IOperationFilter

    {

        /// <summary>

        /// 應用

        /// </summary>

        /// <param name="operation"></param>

        /// <param name="schemaRegistry"></param>

        /// <param name="apiDescription"></param>

        public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription)

 

        {

            if (operation.parameters == null)

                operation.parameters = new List<Parameter>();

            var filterPipeline = apiDescription.ActionDescriptor.GetFilterPipeline(); //判斷是否添加權限過濾器

            var isAuthorized = filterPipeline.Select(filterInfo => filterInfo.Instance).Any(filter => filter is IAuthorizationFilter); //判斷是否允許匿名方法

            var allowAnonymous = apiDescription.ActionDescriptor.GetCustomAttributes<AllowAnonymousAttribute>().Any();

            if (isAuthorized && !allowAnonymous)

            {

                operation.parameters.Add(new Parameter { name = "TOKEN", @in = "header", description = "安全", required = false, type = "string" });

            }

        }

}

在SwaggerConfig類的方法EnableSwagger中加入以下代碼:

c.OperationFilter<HeaderTokenFilter>();

運行項目,可以看到在方法請求參數中多了一個TOKEN參數,效果如下:

 

 

在方法請求時輸入Token值,我的Token驗證值為了測試方便設置成了123456,效果如下:

 

但是這樣的話每次請求的時候都要給TOKEN參數賦值有點小麻煩,我們可以看到Swagger UI界面的最頂部有一個api_key,開啟它就可以解決這個問題。

在SwaggerConfig類的方法EnableSwagger中加入以下代碼:

c.ApiKey("TOKEN")
.Description("API Key Authentication")
.Name("TOKEN")
.In("header");

在SwaggerConfig類的方法EnableSwaggerUi中加入以下代碼:

c.EnableApiKeySupport("TOKEN", "header");

運行項目,效果如下:

 

五、控制器注釋

做完上述步驟基本就算完成了,但是我們也可以看到控制器的注釋並沒有顯示,如果要顯示控制器注釋還需要增加代碼了,在網上找了一段代碼,類名我命名為SwaggerCacheProvider,如下:

    /// <summary>

    /// swagger顯示控制器的描述

    /// </summary>

    public class SwaggerCacheProvider : ISwaggerProvider

    {

        private readonly ISwaggerProvider _swaggerProvider;

        private static ConcurrentDictionary<string, SwaggerDocument> _cache = new ConcurrentDictionary<string, SwaggerDocument>();

        private readonly string _xml;

        /// <summary>

        /// 

        /// </summary>

        /// <param name="swaggerProvider"></param>

        /// <param name="xml">xml文檔路徑</param>

        public SwaggerCacheProvider(ISwaggerProvider swaggerProvider, string xml)

        {

            _swaggerProvider = swaggerProvider;

            _xml = xml;

        }

 

        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 ConcurrentDictionary<string, string> GetControllerDesc()

        {

            string xmlpath = _xml;

            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;

        }

}

在SwaggerConfig類的方法EnableSwagger中加入以下代碼:

c.CustomProvider((defaultProvider) => new SwaggerCacheProvider(defaultProvider, $"{AppDomain.CurrentDomain.BaseDirectory}/bin/Swagger.xml"));

運行項目,效果如下:

 


免責聲明!

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



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