基于 .net core 8.0 的 swagger 文档优化分享-根据命名空间分组显示

net,core,swagger · 浏览次数 : 175

小编点评

本文将介绍如何在 .NET Core 项目中使用 Swashbuckle.AspNetCore 插件生成 Swagger 文档,并针对接口数量增加导致性能下降的问题提出优化方案。 一、安装及使用 Swashbuckle.AspNetCore 1. 安装相关包:在 .NET Core 项目中添加以下包引用: ``` ``` 2. 配置项目:在项目根目录下创建 XML 生成路径,并更新项目文件中的 `DocumentationFile` 属性。 ``` bin\$(MSBuildProjectName).xml ``` 3. 服务配置:使用 SwaggerGen 核心类库配置基础设置,如环境、模型标识、唯一标识、解析 XML 等。 二、优化 Swagger 文档 1. 分组返回:根据业务需求,将 Swagger 文档按照模块进行分组,以便更清晰地展示各个接口。 2. 优化接口返回:为了避免接口响应超时和渲染卡顿问题,可以在发布应用后,请求一个接口将 Swagger 的 JSON 文件生成到本地,后续访问直接读取。 3. 自定义分组匹配:使用自定义 `DocInclusionPredicate` 函数,根据需要分组的接口进行匹配,以优化文档显示。 三、总结 本文介绍了在 .NET Core 项目中使用 Swashbuckle.AspNetCore 插件生成 Swagger 文档的方法,并针对接口数量增加导致性能下降的问题提出了优化方案。通过合理配置,可以更好地展示和管理 API 文档,提高开发效率和用户体验。

正文

前言

公司项目是是微服务项目,网关是手撸的一个.net core webapi 项目,使用 refit 封装了 20+ 服务 SDK,在网关中进行统一调用和聚合等处理,以及给前端提供 swagger 文档
在我两年前进公司的时候,文档还能够顺滑的打开,在去年的时候文档只能在本地打开,或者访问原始的 swagger 页面,knife4j 的页面更是打不开一点,于是想办法对此进行了优化

.net core 项目中使用 Swashbuckle.AspNetCore 生成 SwaggerUI

首先再记录一下安装及使用,之前也分享过 Swashbuckle.AspNetCore 的使用,不过版本比较老了,本次演示用的示例版本为 .net core 8.0,从安装使用开始分享一二

安装包

  • 新建.net core 项目
  • 添加 Swashbuckle.AspNetCore 相关包引用
  • 设置项目 xml 生成路径,组件将根据 xml 解析接口相关信息
  <ItemGroup>
    <PackageReference Include="Swashbuckle.AspNetCore" Version="6.6.2" />
    <PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="6.6.2" />
    <PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="6.6.2" />
  </ItemGroup>
	<PropertyGroup>
		<DocumentationFile>bin\$(MSBuildProjectName).xml</DocumentationFile>
	</PropertyGroup>

服务配置

  • 一些基础配置使用备忘
    • 配置文档信息 c.SwaggerDoc
    • 配置环境 c.AddServer
    • 配置模型标识 c.CustomSchemaIds
    • 配置唯一标识 c.CustomOperationIds
    • 配置解析 xml c.IncludeXmlComments
    • 启用数据注解 c.EnableAnnotations [SwaggerOperation]
  • 完整配置如下
//框架初始化巴拉巴拉xxx
builder.Services.AddControllers();
//配置 swagger
UseSwagger(builder.Services);

/// <summary>
/// Swagger 注入配置
/// </summary>
/// <param name="services"></param>
/// <returns></returns>
void UseSwagger(IServiceCollection services)
{
    services.AddSwaggerGen(c =>
    {
        //配置文档信息
        c.SwaggerDoc("v1", new OpenApiInfo
        {
            Title = "swagger接口文档测试",
            Description = "这是一个文档",
            Version = "v1",
        });
        //配置环境
        c.AddServer(new OpenApiServer()
        {
            Url = "",
            Description = "本地"
        });
        //配置模型标识,默认type.Name,名称一样,不同明明空间会报错,所以改成FullName,加上命名空间区分
        c.CustomSchemaIds(type => type.FullName);
        //配置唯一标识
        c.CustomOperationIds(apiDesc =>
        {
            var controllerAction = apiDesc.ActionDescriptor as ControllerActionDescriptor;
            return controllerAction.ControllerName + "-" + controllerAction.ActionName;
        });
        //解析站点下所有xml,一般加自己项目的引用的即可
        foreach (var file in Directory.GetFiles(AppContext.BaseDirectory, "*.xml"))
        {
            c.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, file));
        }
        //启用数据注解
        c.EnableAnnotations(true, true);
    });
}
  • 启用 swagger

RunSwagger(app);

/// <summary>
/// 启用swagger
/// </summary>
/// <param name="app"></param>
void RunSwagger(IApplicationBuilder app)
{
    app.UseSwaggerUI(c =>
    {
        c.SwaggerEndpoint("/v1/api-docs", "V1 Docs");
    });
    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllers();
        endpoints.MapSwagger("{documentName}/api-docs");
        endpoints.MapGet("/v3/api-docs/swagger-config", async (httpContext) =>
        {
            JsonSerializerOptions jsonSerializerOptions = new JsonSerializerOptions
            {
                PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
                IgnoreNullValues = true
            };
            jsonSerializerOptions.Converters.Add(new JsonStringEnumConverter(JsonNamingPolicy.CamelCase, false));
            SwaggerUIOptions _options = new SwaggerUIOptions()
            {
                ConfigObject = new ConfigObject()
                {
                    Urls = new List<UrlDescriptor>
                        {
                            new UrlDescriptor()
                            {
                                Url="/v1/api-docs",
                                Name="V1 Docs"
                            }
                        }
                }
            };
            await httpContext.Response.WriteAsync(JsonSerializer.Serialize(_options.ConfigObject, jsonSerializerOptions));
        });
    });
}

运行

  • 运行后可以看到配置成功,swagger文档已经生成

到这里基础的 swagger 配置已可以使用,更深层次的参考官方文档使用即可,接下来才是不一样的东西
随着我们的项目发展,当我们的服务越来越多,接口也越来越多的时候,swagger 就从慢,到打开超时偶尔能打开,到每次都打不开(/api-docs 过大返回超时,渲染卡顿)
这个时候,或者一开始就应该对 swagger 进行分组返回了,优化 /api-docs 接口返回的数据
当然,除了这种方式,还有可以加特效标记的方式,但是几百个服务,加不了一点

分模块返回文档

一开始并没有想到分组显示,因为在本地运行的时候是可以打开的,只是 json 文件较大,于是做了一个优化是每次在发布应用后,请求一个接口去将 swagger 的 json 文件生成到本地,后续访问直接读取,算是暂时解决了打不开的问题,这样用了大半年,实在受不了这个速度,然后平时在看一些开源项目的时候发现是完全可以按自己的规则进行分组的,于是有了这篇文章

为了兼容之前的文档路由,所以还是在原有配置的基础上,配置了其他模块的接口文档
可有两种方式

  • 一种是在原有基础上显示其他分组
  • 一种是单独的 swagger 进行显示

优化修改

  • 先定义好需要分组显示的模块
//设置需要分组的api接口
var groupApis = new List<string>() { "SwaggerTest.Controllers.Test", "SwaggerTest.Controllers.Demo" };
  • UseSwagger 修改部分
  • 重点是这块的自定义,去分组中匹配路由 c.DocInclusionPredicate 官方文档
//配置文档信息
c.SwaggerDoc("v1", new OpenApiInfo
{
    Title = "swagger接口文档测试",
    Description = "这是一个文档",
    Version = "v1",
});
//配置环境
c.AddServer(new OpenApiServer()
{
    Url = "",
    Description = "本地"
});
//模型标识配置,默认type.Name,名称一样,不同明明空间会报错,所以改成FullName,加上命名空间区分
c.CustomSchemaIds(type => type.FullName);
c.CustomOperationIds(apiDesc =>
{
    var controllerAction = apiDesc.ActionDescriptor as ControllerActionDescriptor;
    return controllerAction.ControllerName + "-" + controllerAction.ActionName;
});
//加载注释文件
foreach (var file in Directory.GetFiles(AppContext.BaseDirectory, "*.xml"))
{
    c.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, file));
}
//增加模块接口的注册
groupApis.ForEach(s =>
{
    c.SwaggerDoc(s, new OpenApiInfo
    {
        Title = "api-" + s,
        Description = "api " + s,
        Version = "v1",
    });
});

//启用数据注解
c.EnableAnnotations(true, true);
//自定义分组匹配
c.DocInclusionPredicate((docName, apiDes) =>
{
    if (groupApis.Contains(docName))
    {
        var displayName = apiDes.ActionDescriptor?.DisplayName?.ToLower() ?? string.Empty;
        var existGroup = groupApis.FirstOrDefault(s => displayName.Contains(s.ToLower()));
        return docName == existGroup;
    }

    return true;
});
  • RunSwagger 修改部分
app.UseSwaggerUI(c =>
{
    c.SwaggerEndpoint("/v1/api-docs", "V1 Docs");
    //默认页支持分组
    groupApis.ForEach(s =>
    {
        c.SwaggerEndpoint($"/{s}/api-docs", s);
    });
});
//单独的页面
groupApis.ForEach(s =>
{
    app.UseSwaggerUI(c =>
    {
        c.RoutePrefix = s;
        c.SwaggerEndpoint($"/{s}/api-docs", s);
    });
});
app.UseEndpoints(endpoints =>
{
    SwaggerUIOptions _options = new SwaggerUIOptions()
    {
        ConfigObject = new ConfigObject()
        {
            Urls = new List<UrlDescriptor>
                {
                    new UrlDescriptor()
                    {
                        Url="/v1/api-docs",
                        Name="V1 Docs"
                    }
                }.Concat(groupApis.Select(s => new UrlDescriptor()
                        {
                            Url = $"/{s}/api-docs",
                            Name = s
                        }).ToList())
        }
    };
})

修改完成后,可以结合自己业务来定义需要单独显示分组,最近又基于此加了一个开放平台的接口,独立于正常网关,单独提供出去,一切都是刚刚好~

后语

如果有更好的方式,欢迎分享
若有错误,欢迎指出,谢谢

相关文档

与基于 .net core 8.0 的 swagger 文档优化分享-根据命名空间分组显示相似的内容:

基于 .net core 8.0 的 swagger 文档优化分享-根据命名空间分组显示

之前也分享过 Swashbuckle.AspNetCore 的使用,不过版本比较老了,本次演示用的示例版本为 .net core 8.0,从安装使用开始,到根据命名空间分组显示,十分的有用

C# 8.0 添加和增强的功能【基础篇】

.NET Core 3.x 和 .NET Standard 2.1 支持 C# 8.0。本文对此版本的更新内容做了简单的记录,希望有助于你我的理解。

武装你的WEBAPI-OData使用Endpoint

本文属于 OData 系列文章 Introduction 更新: 由于新版的 OData 已经默认使用了 endpoint 模式(Microsoft.AspNetCore.OData 8.0.0),不再需要额外配置,本文已经过时(asp.net core 3.1)。 最近看 OData 的 devb

YiShaAdmin:一款基于.NET Core Web + Bootstrap的企业级快速开发框架

前言 今天大姚给大家分享一款基于.NET Core Web + Bootstrap的企业级快速后台开发框架、权限管理系统,代码简单易懂、界面简洁美观(基于MIT License开源,免费可商用):YiShaAdmin。 项目官方介绍 YiShaAdmin 基于.NET Core Web开发,借鉴了很

一套基于 .NET Core 开发的支付SDK集 - paylink

前言 在我们的日常工作开发中对接一些第三方支付是比较常见的,如最常见的就是支付宝、微信支付的对接。今天给大家推荐一个基于.NET Core开发的支付SDK集:paylink,它极大简化了API调用及通知的处理流程从而大大提供我们的工作生产效率。 运行环境 .NET Core 3.1、.NET 6.0

.NET 高效灵活的API速率限制解决方案

前言 FireflySoft.RateLimit是基于.NET Core和.NET Standard构建,支持多种速率限制算法和策略,包括固定窗口、滑动窗口、漏桶、令牌桶等。通过简单的配置和集成,开发者可以快速地将其应用到现有的Web API、微服务或中间件中,实现对请求的精确控制。 同时,该库还支

基于SqlSugar的开发框架循序渐进介绍(25)-- 基于SignalR实现多端的消息通讯

基于ASP.NET Core SignalR 可以实现客户端和服务器之间进行即时通信。本篇随笔介绍一些SignalR的基础知识,以及结合对SqlSugar的开发框架的支持,实现SignalR的多端处理整合,从而实现Winform客户端,基于Vue3+ElementPlus的BS端整合,后面也可以实现对移动端的SignalR的整合通讯。

Asp .Net Core 系列:基于 Castle DynamicProxy + Autofac 实践 AOP 以及实现事务、用户填充功能

目录什么是 AOP ?.Net Core 中 有哪些 AOP 框架?基于 Castle DynamicProxy 实现 AOPIOC中使用 Castle DynamicProxy实现事务管理实现用户自动填充 什么是 AOP ? AOP(Aspect-Oriented Programming,面向切面

开源一个反sql注入的asp.net core中间件

现在公安有一个专门负责信息安全的部门,前几天公司就收到了一个整改通知,防sql注入的整改。 我们公司开始对网站进行了简单的测试,普通的sql都能检测出来。 但还是被发了整改通知,肯定有些sql注入的方法没测出来,于是我就开始查找了sql注入的手法。 sql注入主要有:基于注释、基于一般错误、基于时间

使用EF 连接 数据库 SQLserver、MySql 实现 CodeFirst

1.新建项目,下载Nuget安装包 创建项目需要注意几点,如果是基于 .net framework 的项目 需要选择 相应版本的 EF, 如果是跨平台则选择EF Core版本。 我这里选择的是 .net framework 版本。红框里面是 实现EF Code First 需要的包。 对应的版本: