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