前言
在 .NET 9 之前,生成 OpenAPI(即 Swagger)文档几乎离不开 Swashbuckle.AspNetCore。它既负责扫描控制器生成 JSON 文档,又顺手提供了那个熟悉的 Swagger UI 界面。然而从 .NET 9 开始,微软将 OpenAPI 文档生成能力 内置进框架(Microsoft.AspNetCore.OpenApi),这彻底改变了三方 UI 工具的定位——它们不再是"文档生成器",而退化(或者说回归)为单纯的 UI 渲染器。
本文基于一个最小演示项目,对比 Swashbuckle、NSwag、Scalar 这三种 UI 工具在 .NET 10 中的现代化集成方式。三个项目业务代码完全一致(都是经典的 WeatherForecast 端点),唯一差异就在于 UI 中间件的接入方式。
一、共同的基础:内置 OpenAPI 文档生成
无论使用哪种 UI,三个项目都使用同一套 .NET 内置 API 来生成 OpenAPI v3 JSON 文档:
// 服务注册
builder.Services.AddOpenApi();
// 路由挂载
app.MapOpenApi(); // 默认暴露在 /openapi/v1.json
这一组合的产出就是一份标准的 OpenAPI v3 JSON,访问地址固定为 /openapi/v1.json。所有三种 UI 工具消费的都是这同一份 JSON——这是理解后续差异的关键前提。
包引用上也只需要这一个:
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="10.0.3" />
二、Swashbuckle:老牌选手的现代用法
2.1 安装
<PackageReference Include="Swashbuckle.AspNetCore" Version="10.2.2" />
2.2 集成代码
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddOpenApi();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.MapOpenApi();
app.UseSwaggerUI(options =>
{
options.SwaggerEndpoint("/openapi/v1.json", "My Open API");
});
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
2.3 关键点
- 没有
AddSwaggerGen()也没有UseSwagger()。这是与传统 Swashbuckle 用法最大的区别。文档生成由 .NET 内置管道负责,Swashbuckle 仅扮演 UI 中间件的角色。 SwaggerEndpoint显式告诉 UI 去哪里加载 JSON——这里指向内置端点/openapi/v1.json,而不是 Swashbuckle 自己生成的端点。- UI 默认挂载在
/swagger路径。
2.4 访问地址
启动后访问 http://localhost:5293/swagger 即可看到经典的 Swagger UI 界面。
三、NSwag:另一种 Swagger UI 风味
3.1 安装
<PackageReference Include="NSwag.AspNetCore" Version="14.7.1" />
3.2 集成代码
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddOpenApi();
// builder.Services.AddOpenApiDocument(); // 这是 NSwag 自己的文档生成器,本项目不启用
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.MapOpenApi();
app.UseSwaggerUi(options =>
{
options.DocumentPath = "/openapi/v1.json";
options.DocumentTitle = "My Open API";
});
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
3.3 关键点
- 注意方法名的细微差别:
UseSwaggerUi(NSwag)vsUseSwaggerUI(Swashbuckle)。一个驼峰一个全大写,写错就找不到方法。 DocumentPath等同于 Swashbuckle 的SwaggerEndpoint,指向内置 JSON。- 注释掉的
AddOpenApiDocument()是 NSwag 的"另一面"——它本身也是一个完整的文档生成器,可以完全绕过Microsoft.AspNetCore.OpenApi独立工作。本项目没启用,是为了让三种工具保持对等比较。
3.4 NSwag 的双重身份
NSwag 比 Swashbuckle 更"重",它实际上包含三块能力:
- 文档生成器(
AddOpenApiDocument) - Swagger UI 渲染器(
UseSwaggerUi,本项目用的这部分) - 客户端代码生成工具(命令行
nswag.exe,可生成 C#/TypeScript 客户端)
在 .NET 10 时代,第一块能力被内置 API 取代后,第二块就是它在本场景下的核心价值。
3.5 访问地址
http://localhost:5027/swagger,UI 外观与 Swashbuckle 几乎一致——毕竟底层都是同一个开源的 Swagger UI 项目。
四、Scalar:现代化的新选手
4.1 安装
<PackageReference Include="Scalar.AspNetCore" Version="2.16.4" />
4.2 集成代码
using Scalar.AspNetCore; // 注意:需要显式 using
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddOpenApi();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.MapOpenApi();
app.MapScalarApiReference();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
4.3 关键点
- 这里用的是
Map*而非Use*,这是路由端点注册,不是中间件。差别在于:中间件按管道顺序匹配所有请求,端点路由只命中特定 URL,性能与语义都更清晰,符合 ASP.NET Core 现代设计。 - 默认无需任何参数。Scalar 会自动去找
/openapi/{documentName}.json,与AddOpenApi()的默认输出天然兼容。 - UI 默认挂载在
/scalar路径。
4.4 Scalar 的差异化卖点
如果说 Swashbuckle 和 NSwag 的 UI 都是同一个传统 Swagger UI 的不同包装,那 Scalar 就是真正"另起炉灶"的一个:
- 现代化设计:三栏布局(导航 / 内容 / 请求示例),视觉风格接近 Stripe、Postman 等商业 API 平台。
- 暗色模式:原生支持,无需额外配置。
- 多语言代码示例:自动为每个端点生成 cURL、JavaScript、Python、Go 等多种语言的请求示例。
- 更好的 Markdown 渲染:在端点描述里写富文本时表现更好。
4.5 访问地址
http://localhost:5115/scalar。
五、三者横向对比
| 维度 | Swashbuckle | NSwag | Scalar |
|---|---|---|---|
| 注册方式 | 中间件(Use*) |
中间件(Use*) |
路由端点(Map*) |
| UI 风格 | 传统 Swagger UI | 传统 Swagger UI | 现代化三栏布局 |
| 暗色模式 | 需自定义 | 需自定义 | 原生支持 |
| 代码示例 | 仅 cURL | 仅 cURL | 多语言 |
| 额外能力 | 几乎仅 UI | 可独立生成文档、生成客户端代码 | 仅 UI(专注体验) |
| 包大小 | 较小 | 较大 | 中等 |
| 学习曲线 | 最低(生态成熟) | 中(配置项多) | 低(默认即用) |
| 配置必填项 | SwaggerEndpoint |
DocumentPath |
无 |
六、如何选型
把这三种工具按场景对号入座,大致是这样:
选 Swashbuckle,如果……
- 团队习惯了传统 Swagger UI,想要最平滑的过渡
- 项目里已经有大量 Swashbuckle 配置(Filter、SchemaFilter 等),延续使用即可
- 想要最稳定、最被广泛验证的方案
选 NSwag,如果……
- 需要在同一工具链里完成 "生成文档 + 渲染 UI + 生成客户端代码" 闭环
- 团队后续可能脱离 .NET 内置管道,需要更精细的文档生成控制
- 偏好基于 .NET 类型反射的更深度自定义
选 Scalar,如果……
- 项目对外提供 API(开发者门户、SaaS 平台等),UI 颜值很重要
- 希望开箱即用获得多语言代码示例,方便集成方
- 不需要 UI 之外的能力(如客户端代码生成)
七、关键认知:UI 与文档生成已经解耦
回过头看,本文最重要的不是"三种工具谁更好",而是 .NET 9+ 带来的架构变化:
OpenAPI 文档的生成已经成为框架内置能力,UI 渲染则是可插拔的独立环节。
这意味着:
- 切换 UI 工具几乎零成本。文档 JSON 的来源不变,换个 NuGet 包、改一两行注册代码即可。
- 可以同时挂载多个 UI。比如内部用 Swagger UI 调试,对外用 Scalar 展示,互不冲突。
- 不必为 UI 库的文档生成能力买单。以前 Swashbuckle 的 Filter、ISchemaFilter 等配置都是为了影响它生成的 JSON——现在那些 JSON 来自框架,你只需要学一套:
AddOpenApi()的配置项。
这是一个值得在团队中普及的认知升级。如果你的 .NET 9/10 项目还在写 AddSwaggerGen() 加一堆 Filter,是时候重新审视技术栈了。