1. 为什么我推荐换掉 Swagger UI1.1 API 文档工具这几年的变化做 .NET 后端开发的同学对 Swagger UI 应该都不陌生。过去好几年里Swashbuckle Swagger UI 几乎是 ASP.NET Core API 文档的默认组合新项目脚手架里自动带上的就是它。但从 .NET 8 开始微软把 OpenAPI 文档生成能力直接收编进了官方框架到了 .NET 9AddOpenApi()已经能独立完成 OpenAPI 文档的生成和暴露。反观 Swashbuckle更新节奏明显跟不上官方脚步很多人在 .NET 9 项目里继续用它多多少少会遇到兼容性上的小别扭。与此同时文档展示层也有新一代工具冒出来。Scalar 就是其中比较有代表性的一款。它本身是一个跨生态的 API 文档平台支持 React、Vue、FastAPI、Express 等多个接入场景对 .NET 这边也单独提供了集成包。装上之后启动项目、打开/scalar/v1你会看到一份完全不像 Swagger UI 的文档页面左侧目录、右侧接口详情明暗主题可以切换还能直接在页面上生成多语言客户端代码。第一次用的时候我确实有一种“原来 API 文档也能做得这么现代”的感觉。这篇文章不打算做那种“Swagger 已死Scalar 当立”的标题党而是想从一个实际迁移过项目的开发者的角度把 Scalar.AspNetCore 的接入过程、核心功能、以及我踩过的坑都讲一遍。无论你是在 .NET 9 新项目里从零接入还是在 .NET 8 老项目里想把 Swagger UI 换掉都可以参考。1.2 Scalar 真正解决了什么问题抛开界面好不好看这种主观感受我觉得 Scalar 相比 Swagger UI 有几个实实在在的改进。第一个是交互调试体验。Swagger UI 的 “Try it out” 功能能用但每次填参数、发请求、看响应总觉得界面有点迟钝。Scalar 的请求面板设计得更紧凑响应区会高亮展示 JSON请求耗时和状态码也很清晰频繁调试接口的时候效率能高不少。第二个是文档的组织和查看方式。Swagger UI 把所有接口平铺在一个长页面里接口多了以后找起来很累。Scalar 默认就是左右分栏布局左侧是 API 分组和接口树点一下就能跳转体验接近现代 API 工具比如 Apifox 或者 Postman 的文档模式。第三个是客户端代码生成能力。这个功能对前后端协作特别有用。后端把接口定义好前端直接在 Scalar 页面里选 TypeScript、Python、C# 等语言几秒钟就能拿到一份可用的客户端代码省得自己手写 HTTP 请求封装。还有一个容易被忽略的点Scalar.AspNetCore 底层依赖的是微软官方 OpenAPI 文档生成器而不是某个第三方框架定义的规范。也就是说你拿到的是标准 OpenAPI 文档Scalar 只是负责把它渲染成交互页面。这层解耦意味着文档数据的获取可以走官方通道减少了一层对旧框架的依赖。1.3 和常见方案放在一起看为了给还没决定换不换的同学一个直观参考我把 Swagger UI、ReDoc 和 Scalar 在几个关键维度上做了个对比。对比项Swagger UIReDocScalar交互调试支持体验一般基本不支持支持体验顺畅界面观感传统、偏旧阅读型文档效果好现代美观多主题 / 暗色模式弱有限支持丰富多语言客户端代码生成无无内置认证方案配置需要手工扩展有限内置多 OpenAPI 文档切换支持但体验一般支持支持体验好与 .NET 9 官方 OpenAPI 的契合度一般有兼容成本需要适配高官方方案ReDoc 的阅读体验确实好长文档排版清晰但如果你需要现场调试接口、带上 Token 发请求它就有点使不上劲了。Scalar 更像是一个把“阅读文档”和“调试接口”合并在一起的工具这也是我最终选择它的核心原因。2. 项目接入从零到能用只要两步2.1 安装与最小配置接入 Scalar.AspNetCore 非常简单和装其他 NuGet 包一样在项目目录里执行dotnet add package Scalar.AspNetCore如果你的项目用的是 .NET 9并且还没有配置官方的 OpenAPI 生成那么最小代码长这样var builder WebApplication.CreateBuilder(args); builder.Services.AddOpenApi(); builder.Services.AddScalar(); var app builder.Build(); if (app.Environment.IsDevelopment()) { app.MapOpenApi(); } app.MapScalarApiReference(); app.Run();跑起来之后浏览器访问/scalar/v1就能看到 Scalar 的文档页面了。/openapi/v1.json这个地址也会同时生效暴露的是标准 OpenAPI JSON 文档你可以拿给任何支持 OpenAPI 的工具去消费不只是 Scalar 自己。这里我说一下为什么建议把MapOpenApi()放在IsDevelopment()判断里。MapOpenApi()会把 OpenAPI JSON 文档暴露到公网如果生产环境没有额外鉴权等于把接口结构全部公开了。虽然很多团队觉得接口文档公开无所谓但从安全角度讲默认只在开发环境暴露更稳妥。Scalar UI 本身倒是可以一直开着因为就算 UI 出来了没有 OpenAPI 文档数据它也只是个空壳。2.2 从 Swashbuckle 迁移的注意点如果你的项目之前用的是 Swashbuckle想换成 Scalar那其实不算迁移更像是“去 Swashbuckle 化”。需要动的地方主要在Program.cs原本的写法一般是builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); // ... app.UseSwagger(); app.UseSwaggerUI();换成 Scalar 之后builder.Services.AddOpenApi(); builder.Services.AddScalar(); // ... app.MapOpenApi(); app.MapScalarApiReference();注意AddEndpointsApiExplorer()在 .NET 9 里可以不用再手动调了因为官方 OpenAPI 生成器已经默认注册了相关服务。但有一点需要特别提醒Swashbuckle 会自动扫描控制器和 Minimal API 的 XML 注释来生成接口说明而官方 OpenAPI 生成器默认不会读取 XML 注释。所以如果你之前依赖GenerateDocumentationFile生成的注释迁移之后很可能发现接口说明“消失”了。解决办法是用文档转换器补上。后面 3.3 节我会写详细做法。另外一个容易忽略的地方是Swashbuckle 支持通过[SwaggerOperation]、[SwaggerResponse]这些特性来补充接口元数据迁移到官方 OpenAPI 生成器之后这些特性都不再生效。如果你在项目里大量使用了这类特性迁移成本会比想象中高需要评估清楚再动。2.3 在 .NET 8 及更早版本上的接入方式很多项目还在 .NET 8 上官方 OpenAPI 生成器其实在 .NET 8 就已经提供了基础版本只是功能不如 .NET 9 完整。在 .NET 8 项目里接入 Scalar 的方式是dotnet add package Microsoft.AspNetCore.OpenApi --version 8.0.x dotnet add package Scalar.AspNetCore然后代码和上面类似同样调用AddOpenApi()和MapOpenApi()。需要注意的是.NET 8 官方 OpenAPI 生成器支持的文档转换能力相对有限像自定义 Schema 的 ID、架构名称处理这些需要你手动做更多配置。如果你的项目还在 .NET 6 或 .NET 7那就有点尴尬了官方 OpenAPI 生成器不可用Scalar 没有内置解析 Swashbuckle 中间件输出文档的能力。用还是能用前提是你用 Swashbuckle 或 NSwag 先生成 OpenAPI 文档然后把 Scalar 指向这个 JSON 地址。但这样一来集成体验会打折也比不上直接用官方方案来得顺滑。所以如果你在 .NET 6/7 项目里我建议先升级框架再考虑换文档工具。3. 这几个功能让我真正留下3.1 现代交互体验与主题定制如果说接入 Scalar 只需要两分钟那让它“长得像你想要的样子”也花不了太久。Scalar 的 UI 有几个配置项非常实用。通过MapScalarApiReference的 options 参数可以控制页面标题、主题、布局、侧边栏显示等。比如app.MapScalarApiReference(options { options .WithTitle(订单服务 API 文档) .WithTheme(ScalarTheme.Purple) .WithDarkModeToggle(true) .WithSidebar(true) .WithDefaultOpenApiDocument(v1); });ScalarTheme枚举提供了多套预设主题比如 Blue、Purple、Saturn、Kepler 等不需要自己写 CSS 就能换肤。WithDarkModeToggle(true)会让页面右上角出现一个明暗切换的按钮这个看起来很简单的功能在长时间对着文档调试接口的时候是真的实用。还有WithLayout可以切换文档的布局模式比如经典的三栏布局或现代布局团队有 UI 洁癖的话可以慢慢调。不过要提醒一句这些配置项的方法名在不同版本的 Scalar.AspNetCore 里可能会变比如早期版本用WithTheme后面版本改成泛型配置项。所以我建议以你安装的包版本对应的文档为准示例代码可以作为参考但不能保证每个版本都能原样编译通过。3.2 内置认证配置调试受保护接口更方便做后端接口免不了有需要登录鉴权才能访问的接口。Swagger UI 里配置 JWT 认证需要写一堆AddSecurityDefinition、AddSecurityRequirement麻烦不说配置错了还排查半天。Scalar 在这方面做得比较顺手。在MapScalarApiReference里可以直接配置认证方案比如常见的 Bearer Tokenapp.MapScalarApiReference(options { options.AddAuthentication( Bearer, authOptions { authOptions.AddBearerAuth( default, schemeOptions { schemeOptions.TokenUrl https://your-auth-server/connect/token; schemeOptions.Scopes.Add(api:read, 读取接口); schemeOptions.Scopes.Add(api:write, 写入接口); }); }); });配置之后Scalar 页面右上角会出现 Authorize 按钮点击后填 Token之后在页面上发请求时会自动把 Token 放进Authorization头。这比在 Postman 里手动复制 Token 省事多了。这里我要重点说一下Scalar 这里的认证配置是 UI 层面的交互配置它把 OpenAPI 安全方案的样式配置好了但你的 OpenAPI 文档里必须声明对应的安全方案否则接口请求可能还是不会被正确标记为“需要认证”。建议在AddOpenApi的文档转换器里同步声明安全方案两边对齐。具体写法我在 4.3 节会展开。3.3 多文档、多环境与客户端代码生成如果你的系统比较复杂一个 API 项目可能同时包含“管理端接口”和“客户端接口”用官方 OpenAPI 生成器可以分成多个文档。Scalar 对多文档的支持也做得不错。在 .NET 9 里注册多个文档的写法是builder.Services.AddOpenApi(admin, options { // 只包含 Admin 相关接口 }); builder.Services.AddOpenApi(public, options { // 只包含 Public 相关接口 });配合options.ShouldInclude和自定义特性可以按控制器或分组过滤接口。Scalar 这边可以通过WithDefaultOpenApiDocument(admin)指定默认打开的文档也可以在界面上手动切换文档。客户端代码生成是我特别喜欢的功能。Scalar 页面里每个接口详情区域都有一个生成代码的入口支持 C#、TypeScript、Python、Java、Go 等多种语言。选好语言后它会自动生成基于 HTTP Client 或 fetch 的调用代码。对于团队里不熟悉接口调用细节的新人来说这个功能基本等于“接口怎么调答案直接抄”。我实际用过几次 TypeScript 和 Python 的生成结果代码结构清晰可以直接塞进项目里改改用省掉了不少重复劳动。3.4 Mock ServerMock Server 是 Scalar 在 UI 层面提供的一个很实用的功能尤其适合前后端并行开发的场景。后端接口还没完全实现时前端需要先调试页面传统做法是自己搭 Mock 服务或者用第三方 Mock 平台。Scalar 直接在文档页面里提供了 Mock Server 的入口可以把 OpenAPI 文档中的示例数据作为模拟响应前端开发直接用这个 Mock 地址调接口。不过这里有个差异要注意Scalar 的 Mock Server 使用方式在不同版本里变化比较大有些版本是页面内直接启动有些版本需要配合 Scalar CLI 使用。我的建议是如果你正好需要这个能力先去查一下当前版本 Scalar.AspNetCore 的 README把说明看清楚再决定要不要依赖它。别把它当成文档功能的全部这个功能更像是锦上添花核心价值还是文档阅读和接口调试。4. 实操中容易踩的坑和排查记录4.1 页面 404多半是少了这一步接入 Scalar 之后最常见的问题就是打开/scalar/v1发现 404。排查思路就两条。第一检查Program.cs里是否调用了app.MapOpenApi()。很多人只写了builder.Services.AddOpenApi()然后忘记了映射端点导致 OpenAPI 文档没有暴露出来。Scalar UI 依赖这个文档地址渲染内容一旦找不到页面自然打不开。第二检查运行环境。如果你用if (app.Environment.IsDevelopment())包裹了MapOpenApi()却在生产环境调试那也会 404。我一般会把MapOpenApi()放在开发环境判断里同时给生产环境做一个独立的鉴权配置这样两边都能访问只是生产环境有额外保护。另外如果你修改了 OpenAPI 的文档名称比如注册成builder.Services.AddOpenApi(admin)那么MapScalarApiReference默认还是会找名为v1的文档导致渲染空白。这时候需要用WithDefaultOpenApiDocument(admin)显式指定。4.2 中文乱码问题Scalar 的 UI 默认语言是英文。如果你在AddOpenApi的文档转换器里把document.Info.Title或document.Info.Description设成了中文理论上应该正常显示但部分版本确实存在标题乱码的情况。我排查过几次最终发现多数是因为标头里Content-Type的charset没有被正确设置为 UTF-8。一个比较直接的解决办法是在MapScalarApiReference之前加一个中间件强制给标头设置编码app.Use(async (context, next) { if (context.Request.Path.StartsWithSegments(/scalar)) { context.Response.Headers[Content-Type] text/html; charsetutf-8; } await next(); }); app.MapScalarApiReference();还有一种更省事的办法标题和描述里尽量只用英文把中文说明放到接口的Description字段上。这个方案治标不治本但确实能规避掉绝大多数乱码问题。如果你在 2025 年之后才看到这篇文章有可能你用的版本已经修复了这个问题建议先试一下再决定要不要加中间件。4.3 认证按钮不显示或者不生效这个问题我也遇到过。明明在MapScalarApiReference里配置了AddAuthentication但页面上就是没有 Authorize 按钮。原因在于Scalar 的认证配置和 OpenAPI 文档里的securitySchemes是配套关系。如果 OpenAPI 文档本身没有声明安全方案Scalar 可能就不会正确渲染。正确做法是在AddOpenApi里添加文档转换器把安全方案补充进去。示例builder.Services.AddOpenApi(options { options.AddDocumentTransformer((document, context, cancellationToken) { var scheme new OpenApiSecurityScheme { Type SecuritySchemeType.Http, Scheme bearer, BearerFormat JWT, Description 请输入 JWT Token }; document.Components ?? new OpenApiComponents(); document.Components.SecuritySchemes[Bearer] scheme; document.SecurityRequirements.Add(new OpenApiSecurityRequirement { [new OpenApiSecurityScheme { Reference new OpenApiReference { Id Bearer, Type ReferenceType.SecurityScheme } }] [] }); return Task.CompletedTask; }); });这样文档里就有了 Bearer 安全方案声明Scalar 的 Authorize 按钮也就能正确识别。配置完了记得 F5 刷新一下页面Scalar 有时会有缓存不刷新看不出来。4.4 接口分组混乱与 XML 注释缺失Scalar 的左侧目录会按照 OpenAPI 文档里的tags来分组接口。如果你在 Controller 上没加[Tags]特性Scalar 默认会用 Controller 名称来分组。对于命名不规范的接口左侧看起来就会很乱。我建议在 Controller 上显式加标签[Tags(订单管理)] [ApiController] [Route(api/[controller])] public class OrderController : ControllerBase { // ... }如果你对接口说明有要求还要处理 XML 注释丢失的问题因为我前面提到官方 OpenAPI 生成器不会自动读 XML 注释。想在文档里显示方法说明目前比较直接的办法是写一个文档转换器扫描控制器上的特性或者通过反射读取 XML 注释再手动设置OpenApiOperation的Summary和Description。这里需要说明微软官方 OpenAPI 生成器在设计上更偏向“从代码结构推导”所以并没有像 Swashbuckle 那样提供一套“默认就读取 XML 注释”的开关。你需要在可维护性和文档丰富度之间做个取舍。如果团队对接口注释要求很高可以先用 Swashbuckle 继续顶一段时间等官方方案成熟了再迁移。4.5 发布到 IIS 和 Docker 时要注意的事Scalar.AspNetCore 本质是静态文件中间件发布到 IIS 和 Docker 都不需要额外配置但有几个细节容易出问题。第一个是路径问题。如果你的站点部署在虚拟目录下比如https://example.com/myapi/需要确保请求/myapi/scalar/v1时路由能正确命中。MapScalarApiReference默认是基于相对路径的正常情况下能工作但如果你的反向代理配置过路径重写Scalar 内部的静态资源请求可能会因为路径前缀对不上而加载失败。第二个是 URL 重写。有些团队会在生产环境加 HTTPS 跳转如果配置不当时 Swagger UI 和 Scalar 页面都会受影响。建议在部署环境里单独验证一下/openapi/v1.json是否能直接访问这个地址正常Scalar 页面基本就不会有大问题。第三个是缓存。Scalar UI 的资源文件会走浏览器缓存如果升级了 Scalar.AspNetCore 版本但浏览器里还停留在旧版界面调试起来会觉得很奇怪。碰到“为什么我改了配置但页面没变化”之类的问题先 CtrlF5 强制刷新再做其他排查。4.6 一个实用的双 UI 切换方案最后分享一个小技巧。如果你担心团队同事用不惯 Scalar或者某天 Scalar 出了奇怪的 bug 影响效率可以同时保留 Swagger UI 和 Scalar通过配置来控制默认使用哪个。思路是在appsettings.json里定义一个配置项{ ApiDoc: { UI: Scalar } }然后在Program.cs里做个简单判断var docUi builder.Configuration[ApiDoc:UI]; app.MapOpenApi(); if (docUi Scalar) { app.MapScalarApiReference(); } if (docUi Swagger) { app.UseSwagger(); app.UseSwaggerUI(); }这样团队可以按自己的习惯选择用哪个 UI迁移过程中不会因为“突然换工具”被打断。等大家都习惯了 Scalar再把 Swagger 相关代码清掉。这种渐进式替换的方式比一次性强行切换要稳妥得多。根据我个人替换下来的体会Scalar.AspNetCore 在 .NET 9 时代已经是一个成熟可用的方案。界面现代化的同时它没有牺牲调试功能的完整性反而补充了代码生成、Mock 等实用能力。如果你还在用 Swagger UI 并且正好也在纠结要不要换我建议找个不忙的周五下午花半小时接上去感受一下大概率你会和我一样换完就不想再换回去了。