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结果的基础教程。测试案例,可以添加笔者公众号,联系作者进行获取。



相关文章
|
7月前
|
JSON 前端开发 JavaScript
解锁JSON的奇妙世界:从基础到高级应用,一文搞懂JSON的妙用(上)
解锁JSON的奇妙世界:从基础到高级应用,一文搞懂JSON的妙用(上)
131 0
解锁JSON的奇妙世界:从基础到高级应用,一文搞懂JSON的妙用(上)
|
7月前
|
存储 JSON NoSQL
解锁JSON的奇妙世界:从基础到高级应用,一文搞懂JSON的妙用(下)
解锁JSON的奇妙世界:从基础到高级应用,一文搞懂JSON的妙用(下)
170 0
|
7月前
|
JSON 程序员 数据格式
深入探索 “JSON for Modern C++“:安装、构建与应用
深入探索 “JSON for Modern C++“:安装、构建与应用
186 0
|
7月前
|
JSON JavaScript 前端开发
解锁JSON的奇妙世界:从基础到高级应用,一文搞懂JSON的妙用(中)
解锁JSON的奇妙世界:从基础到高级应用,一文搞懂JSON的妙用(中)
|
1月前
|
存储 JSON 监控
公司用什么软件监控电脑:JSON 在监控信息交互中的应用探索
在现代企业管理中,电脑监控软件广泛应用于保障信息安全和提升工作效率。JSON(JavaScript Object Notation)因其简洁和易读性,在监控信息的收集、传输和处理中扮演着关键角色。本文介绍了 JSON 在监控数据结构、信息传输及服务器端处理中的具体应用,展示了其在高效监控系统中的重要性。
37 0
|
4月前
|
存储 SQL JSON
【Azure Logic App】微软云逻辑应用连接到数据库,执行存储过程并转换执行结果为JSON数据
【Azure Logic App】微软云逻辑应用连接到数据库,执行存储过程并转换执行结果为JSON数据
【Azure Logic App】微软云逻辑应用连接到数据库,执行存储过程并转换执行结果为JSON数据
|
4月前
|
JSON Java Android开发
Android 开发者必备秘籍:轻松攻克 JSON 格式数据解析难题,让你的应用更出色!
【8月更文挑战第18天】在Android开发中,解析JSON数据至关重要。JSON以其简洁和易读成为首选的数据交换格式。开发者可通过多种途径解析JSON,如使用内置的`JSONObject`和`JSONArray`类直接操作数据,或借助Google提供的Gson库将JSON自动映射为Java对象。无论哪种方法,正确解析JSON都是实现高效应用的关键,能帮助开发者处理网络请求返回的数据,并将其展示给用户,从而提升应用的功能性和用户体验。
110 1
|
4月前
|
JSON 数据格式 Java
化繁为简的魔法:Struts 2 与 JSON 联手打造超流畅数据交换体验,让应用飞起来!
【8月更文挑战第31天】在现代 Web 开发中,JSON 成为数据交换的主流格式,以其轻量、易读和易解析的特点受到青睐。Struts 2 内置对 JSON 的支持,结合 Jackson 库可便捷实现数据传输。本文通过具体示例展示了如何在 Struts 2 中进行 JSON 数据的序列化与反序列化,并结合 AJAX 技术提升 Web 应用的响应速度和用户体验。
131 0
|
6月前
|
JSON 中间件 Java
中间件中OpenAPI (Swagger)
【6月更文挑战第3天】
100 4
|
6月前
|
存储 JSON 关系型数据库
MySQL JSON 类型:功能与应用
MySQL JSON 类型:功能与应用