百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程网 > 正文

关于Swagger优化 swagger是什么意思

yuyutoo 2024-10-12 01:50 2 浏览 0 评论

背景

  尽管.net6已经发布很久了,但是公司的项目由于种种原因依旧基于.net Framework。伴随着版本迭代,后端的api接口不断增多,每次在联调的时候,前端开发叫苦不迭:“小胖,你们的swagger页面越来越卡了,快优化优化!”。
先查看swagger页面加载耗时:


  以上分别是:

  • v1加载了两次
  • 重新编译程序后打开swagger页面,加载v1(api json)竟然耗时两分多钟。
  • 第一次完整加载页面后重新刷新页面,再次查看swagger的耗时,这次明显页面加载速度提升了不少,但依旧不尽人人意,json返回后渲染耗时太久。

探察&解决

  swagger加载的卡慢问题,萌生了优化swagger的想法,刚开始按传统技能在网络上搜索了一大圈依旧未找到解决方案。幸好swashbuckle开源,还能自己动手分析了。先下载好源码GitHub - domaindrivendev/Swashbuckle.WebApi: Seamlessly adds a swagger to WebApi projects!

一、先看看v1加载慢,却要加载两次

  从上面的图上不难发现第二次v1的加载是跟在lang.js后面,而lang.js实际上就是用来做汉化。打开项目中这个文件

  原来是为了添加控制器注释,重新访问后端取一次接口文档。在查看了源码js后,得到一个更简单的方式,页面的汉化翻译,是在数据取完页面已经渲染后才进行的,可直接使用window.swaggerApi.swaggerObject.ControllerDesc。

 setControllerSummary: function () {
        var summaryDict = window.swaggerApi.swaggerObject.ControllerDesc;
        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);
                }
            }
        });
    },

  修改完文件以后,再看看页面的加载,已经不会重复去访问v1。

二、接下来处理v1加载慢

  先看看项目的的swagger配置:

            GlobalConfiguration.Configuration
                .EnableSwagger(c =>
                {
                    c.IncludeXmlComments(GetXmlCommentsPath(thisAssembly.GetName().Name));
                    c.IncludeXmlComments(GetXmlCommentsPath("xxxx.Api.Dto"));
                    c.SingleApiVersion("v1", "xxxx.Api");
                    c.CustomProvider((defaultProvider) => new CachingSwaggerProvider(defaultProvider));
                })

  配置不多,其中有个CachingSwaggerProvider,实现了GetSwagger方法自定义返回数据,在这个方法里可以得知,实际上对api文档是有做缓存处理,v1加载的数据也就是这个SwaggerDocument。这也意味着,v1加载慢的原因出在这里。

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 as Swashbuckle.Swagger.SwaggerGenerator).GetSwagger(rootUrl, apiVersion);
                srcDoc.vendorExtensions = new Dictionary<string, object> { { "ControllerDesc", GetControllerDesc() } };
                _cache.TryAdd(cacheKey, srcDoc);
            }
            return srcDoc;
        }

  调试程序的时候,swashbuckle提供的GetSwagger方法占据了大量的耗时。将源码Swashbuckle.Core引用进来,重新打开swagger时会有个小问题,资源文件都报404错误,这个是因为嵌入资源文件没有找到

  <ItemGroup>
    <EmbeddedResource Include="..\swagger-ui\dist\**\*.*">
      <LogicalName>%(RecursiveDir)%(FileName)%(Extension)</LogicalName>
      <InProject>false</InProject>
    </EmbeddedResource>
  </ItemGroup>

  根据路径查看,swagger-ui下是空白的。将从其他地方找到的或者从反编译文件里整理出来的文件放到该目录下,并将swagger-ui作为依赖项,重新编译项目后swagger页面加载资源文件就正常了。(如果有遇到依旧找不到资源文件的情况,重新再添加一次依赖项编译项目即可)

  接下来就可以开始调试了,经过一番波折,最终将元凶定位到了SwaggerGenerator中GetSwagger方法里获取paths这个地方,实际上就是在使用CreatePathItem的时候耗时过久

   var paths = GetApiDescriptionsFor(apiVersion)
                .Where(apiDesc => !(_options.IgnoreObsoleteActions && apiDesc.IsObsolete()))
                .OrderBy(_options.GroupingKeySelector, _options.GroupingKeyComparer)
                .GroupBy(apiDesc => apiDesc.RelativePathSansQueryString())
                .ToDictionary(group => "/" + group.Key, group => CreatePathItem(group, schemaRegistry));

  刚开始尝试用多线程的方式进行处理,尽管确实能够缩短获取json数据的时间,但依旧有两个问题:

  • 线程不安全,时不时页面会报错
  • 即使能快速返回json数据,页面渲染耗慢的问题依旧未解决。正如前面我们的项目中GetSwagger是使用到缓存的,在重新刷新swagger时,依旧存在卡慢问题。

三、将需返回json数据

  优化swagger加载,需要同时考虑到前端渲染页面以及后端梳理json数据所导致的页面加载慢问题。有什么好的办法么?swashbuckle core版本是支持分组的,但是项目使用的Framework版本不支持,既然不支持,就直接改造源码,按控制器分组,说干就干:

  找到HttpConfigurationExtensions类的EnableSwagger方法,这个方法用来配置路由

public static SwaggerEnabledConfiguration EnableSwagger(
            this HttpConfiguration httpConfig,
            string routeTemplate,
            Action<SwaggerDocsConfig> configure = null)
        {
            var config = new SwaggerDocsConfig();
            if (configure != null) configure(config);
    
    		httpConfig.Routes.MapHttpRoute(
                name: "swagger_docs" + routeTemplate,
                routeTemplate: routeTemplate,
                defaults: null,
                constraints: new { apiVersion = @".+" },
                handler: new SwaggerDocsHandler(config)
            );
			
    		//配置控制器路由
    		string controllRouteTemplate=DefaultRouteTemplate+"/{controller}";
            httpConfig.Routes.MapHttpRoute(
                name: "swagger_docs" + controllRouteTemplate,
                routeTemplate: controllRouteTemplate,
                defaults: null,
                constraints: new { apiVersion = @".+" },
                handler: new SwaggerDocsHandler(config)
            );

            return new SwaggerEnabledConfiguration(
                httpConfig,
                config.GetRootUrl,
                config.GetApiVersions().Select(version => routeTemplate.Replace("{apiVersion}", version)));
        }

  接下来找到SwaggerDocsHandler类,修改SendAsync方法,获取controller,并将controller传递到GetSwagger中

protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            var swaggerProvider = _config.GetSwaggerProvider(request);
            var rootUrl = _config.GetRootUrl(request);
            var apiVersion = request.GetRouteData().Values["apiVersion"].ToString();
            var controller = request.GetRouteData().Values["controller"]?.ToString();
			if (string.IsNullOrEmpty(controller))
            {
                controller = "Account";
            }
    
            try
            {
                var swaggerDoc = swaggerProvider.GetSwagger(rootUrl, apiVersion, controller);
                var content = ContentFor(request, swaggerDoc);
                return TaskFor(new HttpResponseMessage { Content = content });
            }
            catch (UnknownApiVersion ex)
            {
                return TaskFor(request.CreateErrorResponse(HttpStatusCode.NotFound, ex));
            }
        }

  相对应的修改ISwagger接口,以及接口的实现类SwaggerGenerator,增加按Controller筛选

    public interface ISwaggerProvider
    {
        SwaggerDocument GetSwagger(string rootUrl, string apiVersion,string controller);
    }
	SwaggerGenerator的GetSwagger修改:
 var temps = GetApiDescriptionsFor(apiVersion)
                .Where(apiDesc => !(_options.IgnoreObsoleteActions && apiDesc.IsObsolete()));
            if (string.IsNullOrEmpty(controller) == false)
            {
                temps = temps.Where(apiDesc => apiDesc.ActionDescriptor.ControllerDescriptor.ControllerName.ToLower() == controller.ToLower());
            }

            var paths = temps
                .OrderBy(_options.GroupingKeySelector, _options.GroupingKeyComparer)
                .GroupBy(apiDesc => apiDesc.RelativePathSansQueryString())
                .ToDictionary(group => "/" + group.Key, group => CreatePathItem(group, schemaRegistry));

  自己项目中关于ISwagger实现也要修改,然后开始重新编译自己的项目,重新打开swagger页面,页面在后端编译后第一次打开也非常迅速。默认打开的是Account控制器下的接口,如果切换到其他控制器下的接口只需要在url后加入对应的/Controller

四、修改Swagger页面

  以上我们已经把页面的加载慢的问题解决了,但在切换控制器上是否过于麻烦,能不能提升前端开发人员的使用体验,提供一个下拉列表选择是不是更好呢?继续干!

  找到源码目录下的SwaggerUi\CustomAssets\Index.html文件,添加一个id为select_baseUrl的select下拉选择框,并将input_baseurl输入框隐藏

修改swagger-ui-js下的window.SwaggerUi的render方法(要记得将index.html中的swagger-ui-min-js的引用改为swagger-ui-js)加入填充下拉数据的js代码以及添加下拉框触发事件

  找到SwaggerUi.Views.HeaderView,添加下拉事件

  重新编译后,刷新页面试试效果,可以下拉选择分组

文章来自https://www.cnblogs.com/yuluowuhun/p/16155150.html

相关推荐

史上最全的浏览器兼容性问题和解决方案

微信ID:WEB_wysj(点击关注)◎◎◎◎◎◎◎◎◎一┳═┻︻▄(页底留言开放,欢迎来吐槽)●●●...

平面设计基础知识_平面设计基础知识实验收获与总结
平面设计基础知识_平面设计基础知识实验收获与总结

CSS构造颜色,背景与图像1.使用span更好的控制文本中局部区域的文本:文本;2.使用display属性提供区块转变:display:inline(是内联的...

2025-02-21 16:01 yuyutoo

写作排版简单三步就行-工具篇_作文排版模板

和我们工作中日常word排版内部交流不同,这篇教程介绍的写作排版主要是用于“微信公众号、头条号”网络展示。写作展现的是我的思考,排版是让写作在网格上更好地展现。在写作上花费时间是有累积复利优势的,在排...

写一个2048的游戏_2048小游戏功能实现

1.创建HTML文件1.打开一个文本编辑器,例如Notepad++、SublimeText、VisualStudioCode等。2.将以下HTML代码复制并粘贴到文本编辑器中:html...

今天你穿“短袖”了吗?青岛最高23℃!接下来几天气温更刺激……

  最近的天气暖和得让很多小伙伴们喊“热”!!!  昨天的气温到底升得有多高呢?你家有没有榜上有名?...

CSS不规则卡片,纯CSS制作优惠券样式,CSS实现锯齿样式

之前也有写过CSS优惠券样式《CSS3径向渐变实现优惠券波浪造型》,这次再来温习一遍,并且将更为详细的讲解,从布局到具体样式说明,最后定义CSS变量,自定义主题颜色。布局...

柠檬科技肖勃飞:大数据风控助力信用社会建设

...

你的自我界限够强大吗?_你的自我界限够强大吗英文

我的结果:A、该设立新的界限...

行内元素与块级元素,以及区别_行内元素和块级元素有什么区别?

行内元素与块级元素首先,CSS规范规定,每个元素都有display属性,确定该元素的类型,每个元素都有默认的display值,分别为块级(block)、行内(inline)。块级元素:(以下列举比较常...

让“成都速度”跑得潇潇洒洒,地上地下共享轨交繁华
让“成都速度”跑得潇潇洒洒,地上地下共享轨交繁华

去年的两会期间,习近平总书记在参加人大会议四川代表团审议时,对治蜀兴川提出了明确要求,指明了前行方向,并带来了“祝四川人民的生活越来越安逸”的美好祝福。又是一年...

2025-02-21 16:00 yuyutoo

今年国家综合性消防救援队伍计划招录消防员15000名

记者24日从应急管理部获悉,国家综合性消防救援队伍2023年消防员招录工作已正式启动。今年共计划招录消防员15000名,其中高校应届毕业生5000名、退役士兵5000名、社会青年5000名。本次招录的...

一起盘点最新 Chrome v133 的5大主流特性 ?

1.CSS的高级attr()方法CSSattr()函数是CSSLevel5中用于检索DOM元素的属性值并将其用于CSS属性值,类似于var()函数替换自定义属性值的方式。...

竞走团体世锦赛5月太仓举行 世界冠军杨家玉担任形象大使

style="text-align:center;"data-mce-style="text-align:...

学物理能做什么?_学物理能做什么 卢昌海

作者:曹则贤中国科学院物理研究所原标题:《物理学:ASourceofPowerforMan》在2006年中央电视台《对话》栏目的某期节目中,主持人问过我一个的问题:“学物理的人,如果日后不...

你不知道的关于这只眯眼兔的6个小秘密
你不知道的关于这只眯眼兔的6个小秘密

在你们忙着给熊本君做表情包的时候,要知道,最先在网络上引起轰动的可是这只脸上只有两条缝的兔子——兔斯基。今年,它更是迎来了自己的10岁生日。①关于德艺双馨“老艺...

2025-02-21 16:00 yuyutoo

取消回复欢迎 发表评论: