zoukankan      html  css  js  c++  java
  • 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"));

    运行项目,效果如下:

     

  • 相关阅读:
    js面对对象编程
    MyBatis -- 一步步教你使用MyBatis
    简洁常用权限系统的设计与实现(一):构造权限菜单树的N(N>=4)种方法
    2014年工作中遇到的20个问题:181-200
    2014年工作中遇到的20个问题:181-200
    老雷:思儿壮志小诗一首(老爸写得都比我好,让我这个文艺青年情何以堪)(家人对幸福美好生活的追求,就是我的奋斗目标)
    老雷:思儿壮志小诗一首(老爸写得都比我好,让我这个文艺青年情何以堪)(家人对幸福美好生活的追求,就是我的奋斗目标)
    雷观(十三):功能优先,开发与重构并举,性能殿后
    雷观(十三):功能优先,开发与重构并举,性能殿后
    URL传递中文参数,大坑一枚,Windows与Linux效果竟然不一致(两种解决方法)
  • 原文地址:https://www.cnblogs.com/hsybs/p/11172109.html
Copyright © 2011-2022 走看看