在 C#/.NET Core 的 Web API 中使用 Swagger 按模块和版本分组并实现排序

news2024/12/28 20:21:26

文章目录

  • 前言
  • 步骤一:安装 Swashbuckle.AspNetCore
  • 步骤二:创建自定义特性
  • 步骤三:配置 Swagger 生成文档
  • 步骤四:标记控制器和方法
  • 总结


前言

在开发 RESTful API 时,良好的文档是必不可少的。Swagger 是一种广泛使用的 API 文档工具,可以帮助我们生成交互式的 API 文档。然而,当项目规模增大,API 数量众多时,我们需要将 API 按照模块和版本进行分组,以便更好地管理和查找。本文将介绍如何在 .NET Core Web API 中使用 Swagger 按模块和版本分组,并使用自定义特性实现这一目标。


步骤一:安装 Swashbuckle.AspNetCore

首先,我们需要安装 Swashbuckle.AspNetCore 包,这是 .NET Core 中用于集成 Swagger 的库。可以在项目的根目录下运行以下命令进行安装:

dotnet add package Swashbuckle.AspNetCore

步骤二:创建自定义特性

为了实现按模块和版本分组,我们需要创建一个自定义特性 ApiDescriptionAttribute。这个特性将用于标记我们的控制器,并包含模块名称、版本号和描述信息。

using Microsoft.AspNetCore.Mvc.ApiExplorer;

namespace WebApplication.ApiAttributes
{
    public class ApiDescriptionAttribute : Attribute, IApiDescriptionGroupNameProvider
    {
        public ApiDescriptionAttribute(string title, string? version = null, string? desc = null, int position = int.MaxValue)
        {
            GroupName = version != null ? $"{title}-{version}" : title;
            Title = title;
            Version = version;
            Description = desc;
            Position = position;
        }

        /// <summary>
        /// 分组名称
        /// </summary>
        public string? GroupName { get; set; }
        /// <summary>
        /// Swagger 标题
        /// </summary>
        public string? Title { get; set; }
        /// <summary>
        /// 版本号
        /// </summary>
        public string? Version { get; set; }
        /// <summary>
        /// 描述
        /// </summary>
        public string? Description { get; set; }
        /// <summary>
        /// 分组顺序
        /// </summary>
        public int Position { get; set; }
    }
}

步骤三:配置 Swagger 生成文档

接下来,我们需要在 Program.cs 中配置 Swagger,使其能够根据我们的自定义特性生成多个文档。

public static void Main(string[] args)
{
    var builder = WebApplication.CreateBuilder(args);
    
    // 添加服务到容器
    builder.Services.AddControllers();
    builder.Services.AddEndpointsApiExplorer();
    builder.Services.AddSwaggerGen(options =>
    {
        // 根据模块和版本生成多个文档
        var apiAssembly = Assembly.GetExecutingAssembly();
        var apiDescriptions = apiAssembly.GetTypes()
            .Where(t => t.GetCustomAttributes<ApiDescriptionAttribute>().Any())
            .Select(t => t.GetCustomAttribute<ApiDescriptionAttribute>())
            .Distinct();

        foreach (var desc in apiDescriptions)
        {
            if (desc != null)
            {
                if (string.IsNullOrEmpty(desc.Version))
                {
                    options.SwaggerDoc($"{desc.Title}", new OpenApiInfo { Title = $"{desc.Title} API", Version = desc.Version, Description = desc.Description, });
                }
                else
                {
                    options.SwaggerDoc($"{desc.Title}-{desc.Version}", new OpenApiInfo
                    {
                        Title = $"{desc.Title} API",
                        Version = desc.Version,
                        Description = desc.Description,
                    });
                }
            }
        }
        //没有加特性的分到这个NoGroup上
        options.SwaggerDoc("NoGroup", new OpenApiInfo
        {
            Title = "无分组"
        });
        //判断接口归于哪个分组
        options.DocInclusionPredicate((docName, apiDescription) =>
        {
            if (docName == "NoGroup")
            {
                //当分组为NoGroup时,只要没加特性的都属于这个组
                return string.IsNullOrEmpty(apiDescription.GroupName);
            }
            else
            {
                return apiDescription.GroupName == docName;
            }
        });
    });

    var app = builder.Build();

    // 配置 HTTP 请求管道
    if (app.Environment.IsDevelopment())
    {
        app.UseSwagger();
        app.UseSwaggerUI(options =>
        {
            // 根据模块和版本生成多个文档
            var apiAssembly = Assembly.GetExecutingAssembly();
            var apiDescriptions = apiAssembly.GetTypes()
                .Where(t => t.GetCustomAttributes<ApiDescriptionAttribute>().Any())
                .Select(t => t.GetCustomAttribute<ApiDescriptionAttribute>())
                .OrderBy(t => t?.Position ?? int.MaxValue).ThenBy(t => t?.Title).ThenBy(t => t?.Version)
                .Distinct();

            foreach (var desc in apiDescriptions)
            {
                if (desc != null)
                {
                    if (string.IsNullOrEmpty(desc.Version))
                    {
                        options.SwaggerEndpoint($"/swagger/{desc.Title}/swagger.json", $"{desc.Title} API");
                    }
                    else
                    {
                        options.SwaggerEndpoint($"/swagger/{desc.Title}-{desc.Version}/swagger.json", $"{desc.Title} API {desc.Version}");
                    }
                }
            }

            options.SwaggerEndpoint("/swagger/NoGroup/swagger.json", "无分组");
        });
    }

    app.UseHttpsRedirection();
    app.UseAuthorization();
    app.MapControllers();
    app.Run();
}

步骤四:标记控制器和方法

使用我们创建的 ApiDescriptionAttribute 来标记控制器和方法。以下是一些示例:

using Microsoft.AspNetCore.Mvc;
using WebApplication.ApiAttributes;

namespace WebApplication.Controllers
{
    [ApiDescription("ModuleA", "v1", "A模组测试")]
    [Route("api/[controller]")]
    [ApiController]
    public class ModuleA1Controller : ControllerBase
    {
        [HttpGet]
        public IActionResult Get()
        {
            return Ok("Module A, Version 1");
        }
    }
}
namespace WebApplication.Controllers
{
    [ApiDescription("ModuleA", "v2")]
    [Route("api/[controller]")]
    [ApiController]
    public class ModuleA2Controller : ControllerBase
    {
        [HttpGet]
        public IActionResult Get()
        {
            return Ok("Module A, Version 2");
        }
    }
}
namespace WebApplication.Controllers
{
    [ApiDescription("ModuleB")]
    [Route("api/[controller]")]
    [ApiController]
    public class ModuleBController : ControllerBase
    {
        [HttpGet]
        public IActionResult Get()
        {
            return Ok("Module B, 仅有Title");
        }
    }
}
namespace WebApplication.Controllers
{
    [ApiDescription("ModuleC")]
    [Route("api/[controller]")]
    [ApiController]
    public class ModuleC1Controller : ControllerBase
    {
        [HttpGet]
        public IActionResult Get()
        {
            return Ok("Module C1, 多Controller共用分组");
        }
    }
}
namespace WebApplication.Controllers
{
    [ApiDescription("ModuleC")]
    [Route("api/[controller]")]
    [ApiController]
    public class ModuleC2Controller : ControllerBase
    {
        [HttpGet]
        public IActionResult Get()
        {
            return Ok("Module C2, 多Controller共用分组");
        }
    }
}
namespace WebApplication.Controllers
{
    [ApiDescription("ModuleD", desc: "D模组测试")]
    [Route("api/[controller]")]
    [ApiController]
    public class ModuleDController : ControllerBase
    {
        [HttpGet]
        public IActionResult Get()
        {
            return Ok("Module D, 指定参数设置");
        }
    }
}
namespace WebApplication.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class ModuleNoGroupController : ControllerBase
    {
        [HttpGet]
        public IActionResult Get()
        {
            return Ok("Module NoGroup");
        }
    }
}
namespace WebApplication.Controllers
{
    [ApiDescription("Position A", desc: "指定顺序", position: 1)]
    [Route("api/[controller]")]
    [ApiController]
    public class PositionAController : ControllerBase
    {
        [HttpGet]
        public IActionResult Get()
        {
            return Ok("Position A, 指定Swagger分组顺序");
        }
    }
}
namespace WebApplication.Controllers
{
    [ApiDescription("Position Z", desc: "指定顺序", position: 0)]
    [Route("api/[controller]")]
    [ApiController]
    public class PositionZController : ControllerBase
    {
        [HttpGet]
        public IActionResult Get()
        {
            return Ok("Position Z, 指定Swagger分组顺序");
        }
    }
}
namespace WebApplication.Controllers
{
    [Route("[controller]")]
    [ApiDescription("天气", "v1", "天气预报")]
    public class WeatherForecastController : ControllerBase
    {
        private static readonly string[] Summaries = new[]
        {
            "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
        };

        private readonly ILogger<WeatherForecastController> _logger;

        public WeatherForecastController(ILogger<WeatherForecastController> logger)
        {
            _logger = logger;
        }

        [HttpGet(Name = "GetWeatherForecast")]
        public IEnumerable<WeatherForecast> Get()
        {
            return Enumerable.Range(1, 5).Select(index => new WeatherForecast
            {
                Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
                TemperatureC = Random.Shared.Next(-20, 55),
                Summary = Summaries[Random.Shared.Next(Summaries.Length)]
            })
            .ToArray();
        }
    }
}

Swagger截图


总结

通过以上步骤,我们可以在 .NET Core Web API 项目中使用 Swagger 按模块和版本分组。这种实现方法使用了自定义特性来标记控制器,并在 Program.cs 中配置了 Swagger 以生成多个文档。这样,在 Swagger UI 中,我们可以根据模块和版本分别查看 API 文档,从而更好地管理和查找 API。这种方法不仅提升了文档的可读性,也增强了项目的可维护性,使开发者和使用者能更方便地交互与理解 API。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2048437.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C/C++ 多线程[1]---线程创建+线程释放+实例

文章目录 前言1. 多线程创建2. 多线程释放3. 实例总结 前言 说来惭愧&#xff0c;写了很久的代码&#xff0c;一个单线程通全部。可能是接触的项目少吧&#xff0c;很多多线程的概念其实都知道&#xff0c;但是实战并没有用上。前段时间给公司软件做一个进度条&#xff0c;涉及…

【Docker系列】Docker 容器时区设置指南

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

中国式现代化产业水平数据集(2011-2021年)

中国式现代化是一种社会主义现代化模式&#xff0c;它具有自己独特的特征和发展路径。这一现代化模式以实现国家富强、民族振兴和人民幸福为目标&#xff0c;强调物质文明与精神文明的协调发展以及人与自然的和谐共生 本文将中国式现代化理念与现代化产业体系相结合&#xff0…

希亦、洁盟、苏泊尔眼镜清洗机哪款好用?热门眼镜清洗机测评总结

随着科学技术的发展&#xff0c;电子设备的升级&#xff0c;越来越多的人开始戴眼镜&#xff0c;而眼镜由于长时间的佩戴&#xff0c;镜框以及镜面都积累了一些灰尘以及人们肉眼所看不见的细菌&#xff0c;但是如果你使用普通的清洁方式去清洗的话肯定是清洗不干净的&#xff0…

记录|Label组件如何控制下边框为直线

目录 前言一、问题描述二、重绘三、效果展示更新时间 前言 参考文章&#xff1a; C# WinForm开发时&#xff0c;仅显示label的下边框怎么解决啊&#xff1f; 验证过&#xff0c;方法可靠。并增加控制绘制的直线粗细的功能。 一、问题描述 C# winform中想只给Label组件的下边框…

佳能FAX-L160G打印机驱动程序安装

佳能FAX-L160G打印机驱动程序安装笔记 1.访问佳能官方网站https://www.canon.com.cn或者相关驱动下载网站&#xff0c;搜索并下载适用于佳能FAX-L160G打印机的驱动程序。 在这里跳出来相关系列&#xff08;没有精确的型号&#xff09;&#xff0c;点进去搜索。 选择第一个驱动…

强化进度慢,武忠祥17堂课怎么听最高效?

武忠祥老师的17堂课怎么看&#xff1f; 我的建议是&#xff0c;别乱看&#xff01; 17讲内容包括15个主要专题和2个附加专题&#xff0c;整体课时量还是相当大的。 以2023版的课时长度为例&#xff0c;即使你全程以1.5倍速观看且不做任何停顿&#xff0c;平均每个专题至少也…

系统项目管理师----高级----前情调研

要成功通过信息系统项目管理师的考试&#xff0c;需要系统性的学习和多方面的准备。以下是详细的学习资源、推荐的书籍和具体的备考步骤。 一、学习资源与网站 1. 官方网站与指南 中国计算机技术职业资格网&#xff1a;这是软考的官方网站&#xff0c;提供考试大纲、政策法规…

Java编程 : 对象的本质

1.1 从机器视角到问题视角的演变 在计算机科学的发展历程中&#xff0c;我们见证了从机器视角到问题视角的深刻转变。这一转变不仅体现了编程语言和技术的进步&#xff0c;更反映了我们对问题解决方式理解的深化。 起初&#xff0c;计算机编程主要依赖于机器视角。汇编语言作…

FPGA串口调试中当电脑串口无法正常通信,设备管理器中“其它设备”位置显示“USB-Blaster”显示感叹号等问题应该怎么解决?

一、问题描述 当我们进行FPGA开发关于串口等试验的设计中&#xff0c;都需要用到串口&#xff0c;而要使用串口就需要先安装串口驱动&#xff0c;但是在安装驱动的过程中会出现各种各样的问题。 这里就出现了如图所示的停产问题&#xff1a; 在图片中我们可以看到FPGA要使…

【promise】Promise的几个关键问题 (三)

Ⅰ-如何改变 promise 的状态? (1) resolve(value): 如果当前是 pending 就会变为 resolved (2) reject(reason): 如果当前是 pending 就会变为 rejected (3) 抛出异常: 如果当前是 pending 就会变为 rejected Ⅱ-一个 promise 指定多个成功/失败回调函数, 都会调用吗? 当 pro…

Jenkins-拉取代码

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、Jenkins环境配置&#xff08;一&#xff09;配置Maven环境&#xff08;1&#xff09;Maven下载&#xff08;2&#xff09;将Maven上传服务器&#xff08;3&…

解决Win11点击睡眠后,唤醒系统,程序全部关闭的问题

网上只是给出修改“在此时间后关闭硬盘”的时间&#xff0c;结果点击【睡眠】&#xff0c;重新唤醒系统&#xff0c;程序依旧全部关闭。这是因为点击【睡眠】时&#xff0c;启动了【休眠】&#xff0c;Win11系统中&#xff0c;休眠再唤醒系统&#xff0c;会导致程序全部关闭。我…

如何在 3 分钟内免费在 AWS 上运行 RStudio

欢迎来到雲闪世界。谈到数据分析&#xff0c;我有理由从本地计算机迁移到云端。最突出的是&#xff0c;您可以运行无限数量的机器&#xff0c;而无需拥有或维护它们。此外&#xff0c;您可以在几分钟内根据需要扩大或缩小规模。如果您选择运行 t2.micro 服务器&#xff0c;您可…

ant design pro 如何去保存颜色

上图 就是实现这样的效果 后端是这样的&#xff0c;这个颜色肯定是存到字符串里的 这是第一步 import mongoose, { Schema, Document } from mongoose;interface IDiscountCard extends Document {title: string;subtitle: string;image: string;shopUrl: string;bgColor: s…

望繁信科技荣膺上海市浦东新区博士后创新实践基地称号

近日&#xff0c;上海望繁信科技有限公司&#xff08;简称“望繁信科技”&#xff09;凭借在大数据流程智能领域的卓越表现&#xff0c;成功入选上海市浦东新区博士后创新实践基地。这一荣誉不仅是对望繁信科技创新能力和技术实力的高度认可&#xff0c;也标志着公司在推动产学…

基于springboot养老院管理系统pf

TOC springboot332基于springboot养老院管理系统pf 第1章 绪论 1.1选题动因 当前的网络技术&#xff0c;软件技术等都具备成熟的理论基础&#xff0c;市场上也出现各种技术开发的软件&#xff0c;这些软件都被用于各个领域&#xff0c;包括生活和工作的领域。随着电脑和笔记…

Hive3:简单ETL实操案例

一、ETL概念简介 ETL&#xff1a; E&#xff0c;Extract&#xff0c;抽取 T&#xff0c;Transform&#xff0c;转换 L&#xff0c;Load&#xff0c;加载 从A抽取数据(E)&#xff0c;进行数据转换过滤(T)&#xff0c;将结果加载到B(L)&#xff0c;就是ETL 二、情景描述 聊天平…

数据结构(邓俊辉)学习笔记】优先级队列 04——完全二叉堆:插入与上滤

文章目录 1. 上滤2. 实例3. 实现4. 效率 1. 上滤 好&#xff0c;接下来我们就来学习在一个完全二叉堆中&#xff0c;如何有效地插入一个新的元素。我们将会看到插入过程中的核心技巧是所谓的 ”上滤“ 过程。 为了在完全二叉堆中引入一个新的词条 e&#xff0c;我们只需在物…

【网络编程】TCP实现网络通信(C语言、Ubuntu实现)

TCP服务器通信模型&#xff1a;&#xff08;分为以下6个步骤&#xff09; 1、sfd socket(); //创建一个用于连接的套接字文件描述符 2、bind(); //为服务器套接字绑定ip地址和端口号&#xff0c;为了让客户端额能够找到服务器 3、l…