• 在 .NET Core 下的 Swagger UI 自定义操作


    1.Swagger UI 是什么?

    Swagger UI 是一个在线的 API 文档生成与测试工具,你可以将其集成在你的 API 项目当中。

    • 支持 API 自动同步生成文档
    • 高度自定义,可以自己扩展功能
    • 前后端分离时方便前端进行 API 接口测试

    2.如何应用?

    这里仅介绍在 DotNetCore 下如何集成 Swagger UI。

    新建一个 API 项目

    Image1

    从 NuGet 下载 Swagger UI 包

    Image2

    配置 Swagger UI

    安装好 Swagger 之后,在需要生成 API 文档的项目当中勾选 XML documentation file.
    Image3
    之后我们需要在 StartUp 当中配置 Swagger 相关的设置。

    public class Startup
    {
    	public Startup(IConfiguration configuration)
    	{
    		Configuration = configuration;
    	}
    
    	public IConfiguration Configuration { get; }
    
    	public void ConfigureServices(IServiceCollection services)
    	{
    		services.AddMvc();
    
    		services.AddSwaggerGen(options =>
    		{
    			options.SwaggerDoc("v1", new Info() { Title = "Swagger Test UI", Version = "v1" });
    			options.CustomSchemaIds(type => type.FullName); // 解决相同类名会报错的问题
    			options.IncludeXmlComments(Path.Combine(Directory.GetCurrentDirectory(), "SwaggerUIDemo.xml")); // 标注要使用的 XML 文档
    		});
    	}
    
    	public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    	{
    		if (env.IsDevelopment())
    		{
    			app.UseDeveloperExceptionPage();
    		}
    
                    app.UseStaticFiles();
    		app.UseSwagger();
    		// 在这里面可以注入
    		app.UseSwaggerUI(options => 
    		{
    			options.InjectOnCompleteJavaScript("/swagger/ui/zh_CN.js"); // 加载中文包
    			options.SwaggerEndpoint("/swagger/v1/swagger.json", "HKERP API V1");
    		});
                    app.UseMvc();
    	}
    }
    

    当然我们直接运行的时候会提示找不到 XML 文件,因为我们使用的是 Path.Combine(Directory.GetCurrentDirectory(), "SwaggerUIDemo.xml"),SO,我们在项目变异的时候手动 COPY 过去即可,编写一个编译事件。
    Image4
    编写完成之后我们运行项目,访问 swagger 的页面就会显示成功了:
    Image5
    当然这里的注释是来自于针对 Value 控制器的注释,Swagger 会自动扫描项目所有的控制器类,并且将其展现在 Swagger UI 当中。

    DIY Swagger UI

    或许到现在 Swagger 已经足够你使用,但是如果你想针对生成的页面进行自定义也是可以的,例如汉化文字?或者在旁边加一个侧边栏?
    这些统统都可以实现,细心的同学可能看到了在 StartUp 类的 Configure 方法当中注入了一个 JavaScript 文件,这个文件会在 Swagger UI 加载完成之后调用。那么我们就可以在这个 JS 里面操作 DOM 元素来进行自定义 UI 了。

    例如汉化:

    'use strict';
    
    /**
     * Translator for documentation pages.
     *
     * To enable translation you should include one of language-files in your index.html
     * after <script src='lang/translator.js' type='text/javascript'></script>.
     * For example - <script src='lang/ru.js' type='text/javascript'></script>
     *
     * If you wish to translate some new texsts you should do two things:
     * 1. Add a new phrase pair ("New Phrase": "New Translation") into your language file (for example lang/ru.js). It will be great if you add it in other language files too.
     * 2. Mark that text it templates this way <anyHtmlTag data-sw-translate>New Phrase</anyHtmlTag> or <anyHtmlTag data-sw-translate value='New Phrase'/>.
     * The main thing here is attribute data-sw-translate. Only inner html, title-attribute and value-attribute are going to translate.
     *
     */
    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')));
            });
        },
    
        _tryTranslate: function (word) {
            return this._words[$.trim(word)] !== undefined ? this._words[$.trim(word)] : word;
        },
    
        learn: function (wordsMap) {
            this._words = wordsMap;
        },
    };
    
    
    /* jshint quotmark: double */
    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();
    });
    

    这里我们如果想要在 Swagger UI 上针对控制器来应用注释的话,就需要自己实现一个 DocumentFiliter,这里我们直接继承自 IDocumentFiliter,来实现一个自定义的 Filiter:

    public class CustomDocumentFiliter : IDocumentFilter
    {
    	public void Apply(SwaggerDocument swaggerDoc, DocumentFilterContext context)
    	{
    		SetContorllerDescription(swaggerDoc.Extensions);
    	}
    
    	private void SetContorllerDescription(Dictionary<string, object> extensionsDict)
    	{
    		string _xmlPath = Path.Combine(Directory.GetCurrentDirectory(), "HKERP.Application.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;
    			XmlNode _summaryNode = null;
    
    			foreach (XmlNode _node in _xmlDoc.SelectNodes("//member"))
    			{
    				_type = _node.Attributes["name"].Value;
    
    				if (_type.StartsWith("T:") && !_type.Contains("T:HKERP.HKERPAppServiceBase") && !_type.Contains("T:HKERP.Net.MimeTypes.MimeTypeNames"))
    				{
    					_summaryNode = _node.SelectSingleNode("summary");
    					string[] _names = _type.Split('.');
    					string _key = _names[_names.Length - 1];
    					if (_key.IndexOf("AppService", _key.Length - "AppService".Length, StringComparison.Ordinal) > -1)
    					{
    						_key = _key.Substring(0, _key.Length - "AppService".Length);
    					}
    
    					if (_summaryNode != null && !string.IsNullOrEmpty(_summaryNode.InnerText) && !_controllerDescDict.ContainsKey(_key))
    					{
    						_controllerDescDict.TryAdd(_key, _summaryNode.InnerText.Trim());
    					}
    				}
    			}
    
    			extensionsDict.TryAdd("ControllerDescription", _controllerDescDict);
    		}
    	}
    }
    

    这里我们直接读取生成的 XML 文档,并且生成一个字典添加到 SwaggerDocument 的 Extensions 属性当中,这样的话,在 Swagger UI 加载的时候,调用的 JSON 接口就回附带上我们添加的内容,这个时候只需要在 JS 文件当中获取并且填充 UI 即可,这里有个示范:

    'use strict';
    
    /**
     * Translator for documentation pages.
     *
     * To enable translation you should include one of language-files in your index.html
     * after <script src='lang/translator.js' type='text/javascript'></script>.
     * For example - <script src='lang/ru.js' type='text/javascript'></script>
     *
     * If you wish to translate some new texsts you should do two things:
     * 1. Add a new phrase pair ("New Phrase": "New Translation") into your language file (for example lang/ru.js). It will be great if you add it in other language files too.
     * 2. Mark that text it templates this way <anyHtmlTag data-sw-translate>New Phrase</anyHtmlTag> or <anyHtmlTag data-sw-translate value='New Phrase'/>.
     * The main thing here is attribute data-sw-translate. Only inner html, title-attribute and value-attribute are going to translate.
     *
     */
    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')));
            });
        },
    
        _tryTranslate: function (word) {
            return this._words[$.trim(word)] !== undefined ? this._words[$.trim(word)] : word;
        },
    
        learn: function (wordsMap) {
            this._words = wordsMap;
        },
    
        setControllerSummary: function () {
            var _str = $("#input_baseUrl").val();
            $.ajax({
                type: "get",
                async: true,
                url: $("#input_baseUrl").val(),
                dataType: "json",
                success: function (data) {
                    //console.log(data)
                    var toggleEndpointList = [];
                    var summaryDict = data.ControllerDescription;
                    var id, controllerName, strSummary;
                    $('body').append('<ul class="leftMenu"></ul>');
                    $("#resources .resource").each(function (i, item) {
                        id = $(item).attr("id");
                        if (id) {
                            controllerName = id.substring(9);
                            strSummary = summaryDict[controllerName];
                            if (strSummary) {
                                console.log($(item))
                                $(item).children(".heading").children("h2").children('a').text(strSummary);
                                $(item).children(".heading").children(".options").prepend('<li class="controller-summary" title="' + strSummary + '">' + strSummary + '</li>');
                                $('.leftMenu').append('<li class="menuLi" title="' + strSummary + '"><a href=""  class="menuLiA">' + strSummary + '</a></li>');
                                toggleEndpointList.push($(item).attr('id'))
                            }
                        }
                    });
                    for (var i = 0; i < document.getElementsByClassName('menuLiA').length;i++){
                        var menuLiA = document.getElementsByClassName('menuLiA');                   
                        menuLiA[i].setAttribute('href', '#'+toggleEndpointList[i])
                    }
                    
                }
            });
        },
    };
    
    
    /* jshint quotmark: double */
    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 () {
        debugger;
        window.SwaggerTranslator.translate();
        window.SwaggerTranslator.setControllerSummary();
    });
    

    效果图:
    Image6

  • 相关阅读:
    quartz 定时调度持久化数据库配置文件
    springboot项目下mvnw文件的作用
    mysql安装版卸载,解压版安装
    idea提示,格式化代码,清除不使用的包快捷键,maven自动导jar包
    JavaScript中call,apply,bind方法
    彻底理解js中this的指向
    Gradle系列之从init.gradle说起
    响应式网页设计简单入门
    开启MySQL远程访问权限 允许远程连接
    https原理
  • 原文地址:https://www.cnblogs.com/myzony/p/8434468.html
Copyright © 2020-2023  润新知