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

运行项目,效果如下:

 

posted @ 2019-07-11 19:24 心怀武侠梦 阅读(...) 评论(...) 编辑 收藏