Swashbuckle源码应用之最后一次修改Swagger中OpenApi.json机会

简介: Swashbuckle源码应用之最后一次修改Swagger中OpenApi.json机会


一、场景

进行Asp.Net Web Api 还是 Asp.Net Core Web Api 项目后端服务开始时,引入Swagger 对应的UI组件时,对于初学者来说,可能就是引入 SwashbuckleNSwag,代码添加如下:

此处以Vs2019进行开发,使用.Net5作为目标框架, 新建一个Asp.Net Core Web Api(空)项目,Web项目中添加依赖:

<Project Sdk="Microsoft.NET.Sdk.Web">
    <!--添加Nuget依赖-->
  <ItemGroup>
    <PackageReference Include="Swashbuckle.AspNetCore" Version="6.1.1" />
  </ItemGroup>
</Project>

Startup.cs的服务配置函数 ConfigureServices 添加服务,同时设置 swagger 文档配置记录。

public void ConfigureServices(IServiceCollection services)
 {
     //注册服务Mvc核心服务,使用ApiExplorer
     services.AddMvcCore().AddApiExplorer();
     //注册SwaggerGen服务
     services.AddSwaggerGen(options => {
         //TODO: 添加文档openapi.json服务配置
         options.SwaggerDoc("diy", new Microsoft.OpenApi.Models.OpenApiInfo { Version="diy",Title="自定义文档" });
     });
 }

Configure函数中配置中间件,具体内容如下:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    // .........省略.......
    // 1、 启用静态文件中间件
    app.UseStaticFiles();
    // 2、 启用swagger中间件
    app.UseSwagger(options => {
        // 2.1、配置自定义路由
        options.RouteTemplate = "doc/{documentName}/swagger.json";
    });
    // 3、启用swagger-ui 中间件及配置
    app.UseSwaggerUI(options => {
        // 3.1、swagger-ui配置文档访问路由前缀
        options.RoutePrefix = "doc";
        // 3.2、配置swagger endpoint 对应访问openapi.json地址
        options.SwaggerEndpoint("diy/swagger.json", "diy");
    });
    // swagger中间件引入在路由中间件之前
    app.UseRouting();
    app.UseEndpoints(endpoints =>
    {
        //模拟api
        endpoints.MapGet("/api/diy", async context =>
        {
            await context.Response.WriteAsync("Hello World!");
        });
    });
}

配置无误后,启动项目,访问http://localhost:5000/doc/index.html,响应结果如下:

访问 http://localhost:5000/api/diy,能够看到对应响应结果:

可以注意到,当前系项目中为空项目,并没有 Api 在文档中显示,如果希望将手动配置的 api 在文档中进行显示时,如果不通过新建对应controlleraction,该如何去做?

app.UseEndpoints(endpoints =>
{
    //模拟api
    endpoints.MapGet("/api/diy", async context =>
    {
        await context.Response.WriteAsync("Hello World!");
    });
});

二、源码解析

没有思路的时候,先不急,先扒拉一下 Swashbuckle 对应源码,源码传送门,先看中间件 SwaggerMiddleware.cs

public class SwaggerMiddleware
{
    private readonly RequestDelegate _next;
    private readonly SwaggerOptions _options;
    private readonly TemplateMatcher _requestMatcher;
    public SwaggerMiddleware(
        RequestDelegate next,
        SwaggerOptions options)
    {
    // ...省略内容...
        // 当构造注入的配置类为null时,内部进行实例化
        _options = options ?? new SwaggerOptions();
        // ...省略内容...
    }
    public async Task Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider)
    {
        // 判定请求的文档名称是否在已配置文档清单中...省略内容...
        try
        {
            // .....省略内容.....
            // 获取swagger类实例
            var swagger = swaggerProvider.GetSwagger(
                documentName: documentName,
                host: null,
                basePath: basePath);
            // 在当前请求上下文中,最后一次修改 Swagger 文档的机会
            foreach (var filter in _options.PreSerializeFilters)
            {
                filter(swagger, httpContext.Request);
            }
      // 判定响应的数据类型json或yaml.....内容省略.....
        }
        catch (UnknownSwaggerDocument)
        {
            RespondWithNotFound(httpContext.Response);
        }
    }
    // ....省略内容....
}

上述代码中,可以注意到,有一组代码注释,非常友好,笔者由软件翻译的中文:

// 在当前请求上下文中,最后一次修改 Swagger 文档的机会
foreach (var filter in _options.PreSerializeFilters)
{
   filter(swagger, httpContext.Request);
}

查看 Swashbuckle.AspNetCore.Swagger.SwaggerOptions 类,可以看出,PreSerializeFilters 是一个委托类型,公有读私有写的集合属性。

public class SwaggerOptions
{
    public SwaggerOptions()
    {
        // 构造进行集合初始化
        PreSerializeFilters = new List<Action<OpenApiDocument, HttpRequest>>();
        SerializeAsV2 = false;
    }
    /// <summary>
    /// Sets a custom route for the Swagger JSON/YAML endpoint(s). Must include the {documentName} parameter
    /// </summary>
    public string RouteTemplate { get; set; } = "swagger/{documentName}/swagger.{json|yaml}";
    //....省略内容.....
    /// <summary>
    /// Actions that can be applied to an OpenApiDocument before it's serialized.
    /// Useful for setting metadata that's derived from the current request
    /// </summary>
    public List<Action<OpenApiDocument, HttpRequest>> PreSerializeFilters { get; private set; }
}

查看中间件扩展函数类 Microsoft.AspNetCore.Builder.SwaggerBuilderExtensions.cs,其中,两个函数存在细微差别。

接受外部自定义 SwaggerOptions实现自定义传入。

/// <summary>
/// Register the Swagger middleware with provided options
/// </summary>
public static IApplicationBuilder UseSwagger(this IApplicationBuilder app, SwaggerOptions options)
{
    return app.UseMiddleware<SwaggerMiddleware>(options);
}

通过配置依赖方式实现中间件配置。

/// <summary>
/// Register the Swagger middleware with optional setup action for DI-injected options
/// </summary>
public static IApplicationBuilder UseSwagger(
    this IApplicationBuilder app,
    Action<SwaggerOptions> setupAction = null)
{
    // 委托参数传入时,SwaggerOptions 由扩展函数内部进行实例化构造
    SwaggerOptions options;
    using (var scope = app.ApplicationServices.CreateScope())
    {
        // 进行实例化构造,与外部配置无关
        options = scope.ServiceProvider.GetRequiredService<IOptionsSnapshot<SwaggerOptions>>().Value;
        // 执行外部配置委托
        setupAction?.Invoke(options);
    }
    return app.UseSwagger(options);
}

以上两个函数中,第一种是配置实例完全有用户使用时自定义实例和配置,第二种为方便函数的操作,添加的委托传入参数,该委托配置,在内部进行SwaggerOptions实例化之后才执行。

三、自定义逻辑

通过添加自定义openapi.json 的生成内容,原有代码进行如下修改:

// 1、 启用静态文件中间件
app.UseStaticFiles();
// 2、 启用swagger中间件
SwaggerOptions options = new SwaggerOptions();
// 2.1、配置openapi.json访问路由
options.RouteTemplate = "doc/{documentName}/swagger.json";
// 2.2、添加自定义openapi.json结构
options.PreSerializeFilters.Add((swaggerDoc, httprequest) => {
    //配置path
    OpenApiPathItem path = new OpenApiPathItem
    {
        Description = "测试服务"
    };
    // 配置请求操作
    OpenApiOperation operation = new OpenApiOperation();
    operation.Tags.Add(new OpenApiTag { Name = "diy", Description = "自定义服务" });
    operation.OperationId = "diy-get";
    operation.Summary = "获取数据";
    path.Operations.Add(OperationType.Get, operation);
    // 设置操作tag
    swaggerDoc.Tags.Add(new OpenApiTag { Name = "diy", Description = "自定义服务" });
    // 添加path到文档中
    swaggerDoc.Paths.Add("/api/diy", path);
});
app.UseSwagger(options);
// 3、启用swagger-ui 中间件及配置
// ...省略内容...

也可以传入委托参数:

// 1、 启用静态文件中间件
app.UseStaticFiles();
// 2、 启用swagger中间件
app.UseSwagger(options => {
    // 2.1、配置openapi.json访问路由
    options.RouteTemplate = "doc/{documentName}/swagger.json";
    // 2.2、添加自定义openapi.json结构
    options.PreSerializeFilters.Add((swaggerDoc, httprequest) =>
    {
        OpenApiPathItem path = new OpenApiPathItem
        {
            Description = "测试服务"
        };
        OpenApiOperation operation = new OpenApiOperation();
        operation.Tags.Add(new OpenApiTag { Name = "diy", Description = "自定义服务" });
        operation.OperationId = "diy-get";
        operation.Summary = "获取数据";
        path.Operations.Add(OperationType.Get, operation);
        swaggerDoc.Tags.Add(new OpenApiTag { Name = "diy", Description = "自定义服务" });
        swaggerDoc.Paths.Add("/api/diy", path);
    });
});
// 3、启用swagger-ui 中间件及配置
// ...省略内容...

四、演示效果

确认无误后,运行项目,访问 http://localhost:5000/doc/index.html,访问效果如下:

在线测试,效果如下:

以上就是本次对于自定义修改openapi.json结果的基础教程。测试案例,可以添加笔者公众号,联系作者进行获取。



相关文章
|
28天前
|
存储 JSON NoSQL
解锁JSON的奇妙世界:从基础到高级应用,一文搞懂JSON的妙用(下)
解锁JSON的奇妙世界:从基础到高级应用,一文搞懂JSON的妙用(下)
|
28天前
|
JSON 前端开发 JavaScript
解锁JSON的奇妙世界:从基础到高级应用,一文搞懂JSON的妙用(上)
解锁JSON的奇妙世界:从基础到高级应用,一文搞懂JSON的妙用(上)
解锁JSON的奇妙世界:从基础到高级应用,一文搞懂JSON的妙用(上)
|
2月前
|
JSON 程序员 数据格式
深入探索 “JSON for Modern C++“:安装、构建与应用
深入探索 “JSON for Modern C++“:安装、构建与应用
41 0
|
28天前
|
JSON JavaScript 前端开发
解锁JSON的奇妙世界:从基础到高级应用,一文搞懂JSON的妙用(中)
解锁JSON的奇妙世界:从基础到高级应用,一文搞懂JSON的妙用(中)
|
3月前
|
Web App开发 JSON JavaScript
SAP UI5 应用程序小技巧 - 一键将 JSON 对象导出成本地 json 文件
SAP UI5 应用程序小技巧 - 一键将 JSON 对象导出成本地 json 文件
25 0
|
4月前
|
存储 JSON JavaScript
Python 的其他应用: 如何使用 Python 处理 JSON 数据?
Python 的其他应用: 如何使用 Python 处理 JSON 数据?
|
23天前
|
XML JSON JavaScript
Java中XML和JSON的比较与应用指南
本文对比了Java中XML和JSON的使用,XML以自我描述性和可扩展性著称,适合结构复杂、需验证的场景,但语法冗长。JSON结构简洁,适用于轻量级数据交换,但不支持命名空间。在Java中,处理XML可使用DOM、SAX解析器或XPath,而JSON可借助GSON、Jackson库。根据需求选择合适格式,注意安全、性能和可读性。
28 0
|
28天前
|
XML JSON JavaScript
使用JSON和XML:数据交换格式在Java Web开发中的应用
【4月更文挑战第3天】本文比较了JSON和XML在Java Web开发中的应用。JSON是一种轻量级、易读的数据交换格式,适合快速解析和节省空间,常用于API和Web服务。XML则提供更强的灵活性和数据描述能力,适合复杂数据结构。Java有Jackson和Gson等库处理JSON,JAXB和DOM/SAX处理XML。选择格式需根据应用场景和需求。
|
2月前
|
XML JSON API
深入解析C++ JSON库:nlohmann::json:: parse的内部机制与应用
深入解析C++ JSON库:nlohmann::json:: parse的内部机制与应用
57 0
|
2月前
|
JSON 前端开发 API
Python中的JSON模块:从基础到高级应用全解析
【2月更文挑战第3天】 Python中的JSON模块:从基础到高级应用全解析
84 6