news 2026/9/14 14:04:17

.NET 9 原生 OpenAPI 生成 + Scalar.AspNetCore:一站式终结 API 文档缺失问题

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
.NET 9 原生 OpenAPI 生成 + Scalar.AspNetCore:一站式终结 API 文档缺失问题

.NET 9 原生 OpenAPI 生成 + Scalar.AspNetCore:一站式终结 API 文档缺失问题

【免费下载链接】scalarScalar is an open-source API platform: 🌐 Modern REST API Client 📖 Beautiful API References ✨ 1st-Class OpenAPI/Swagger Support项目地址: https://gitcode.com/GitHub_Trending/sc/scalar

本文以 Scalar 官方博客《How .NET 9 and Scalar solve the problem of under-documented APIs》为核心脉络,结合本仓库中Scalar.AspNetCore的完整源码、测试与集成文档,系统讲解如何用 .NET 9 内置的 OpenAPI 文档生成能力配合 Scalar 的 ASP.NET 包,在最小改动下自动完成"文档生成 → 文档发布 → 交互式 API 文档 → 可调试的 API 客户端"全链路。读完本文,你将掌握从零接入Scalar.AspNetCore、理解其底层实现原理,以及完成多版本文档、认证预填、OpenAPI 扩展等实战配置的完整方案。

API 文档缺失问题:从"忘了写"到"写不动"

如果你写过 API,下面这段流程大概率不陌生:

  1. 完成并发布了 API 的某项改动;
  2. 没有花时间去写文档;
  3. 忘了重新生成 / 更新 API 文档;
  4. 更没有把新文档发布出去供他人使用。

文档缺失的直接后果是:能用 API 的人变少、过段时间自己回来看代码更困惑、出问题后排错也更困难。Scalar 官方博客指出,这并不是开发者的懒惰——在很多代码库里,文档与真实代码是脱节的,于是"记录、更新、发布"API 文档变成了一份无谓的额外工作。核心问题在于:文档生成工具没有跟上框架演进的步伐,开发者被迫手工维护本应自动化的环节。

.NET 9 的转折:内置 OpenAPI 文档生成

过去,ASP.NET 生态通过Swashbuckle解决 API 文档问题,它能从 ASP.NET Core 应用生成 OpenAPI 文档并自带一套 UI。但 Swashbuckle 长期没有跟上 .NET 版本迭代:没有针对 .NET 8 的正式发布,项目近乎停更(社区 PR 自 2022 年 11 月起便未被合并)。

为填补这一空白,ASP.NET Core 团队在 .NET 9 中移除了 Swashbuckle 的默认集成(详见 dotnet/aspnetcore 仓库 issue #54599),并以内置方案替代:通过Microsoft.AspNetCore.OpenApiMicrosoft.Extensions.ApiDescription.Server两个包提供开箱即用的 OpenAPI 文档生成能力,基于 API 的路由映射自动生成一份 JSON 格式的 OpenAPI 文档。

只需安装上述两个包,并在Program.cs中加入两行代码:

var builder = WebApplication.CreateBuilder(); builder.Services.AddOpenApi(); var app = builder.Build(); app.MapOpenApi(); app.MapGet("/", () => "Hello world!"); app.Run();

运行后,OpenAPI 文档即生成在https://localhost:<port>/openapi/v1.json。注意这里的v1是默认的文档名(documentName),文档名可以作为路由参数传入,这正是后续接入多版本文档的基础。

生成文档只是第一步——它解决了"文档从哪来"的问题,但原始 JSON 规范并不适合人类阅读。把这份文档变成可用的交互式文档,正是 Scalar 工具链的用武之地。

接入 Scalar.AspNetCore:三行代码获得完整 API 文档

Scalar.AspNetCore是本仓库维护的官方 NuGet 包(源码位于 integrations/dotnet/aspnetcore,其入口说明见 README),用于在 ASP.NET Core 应用中直接渲染基于 OpenAPI/Swagger 文档的交互式 API 参考文档。

安装与配置只需两步:

dotnet add package Scalar.AspNetCore

然后在Program.cs中加入using指令与MapScalarApiReference()

using Scalar.AspNetCore; var builder = WebApplication.CreateBuilder(); builder.Services.AddOpenApi(); var app = builder.Build(); app.MapOpenApi(); app.MapScalarApiReference(); app.MapGet("/", () => "Hello world!"); app.Run();

浏览器访问https://localhost:<port>/scalar/v1,即可看到一套完整的 API 文档界面。

在官方集成文档 documentation/integrations/aspnetcore/integration.md 中,还给出了与不同 OpenAPI 生成器配合的标准接法:无论你用的是Microsoft.AspNetCore.OpenApiSwashbuckle.AspNetCore.SwaggerGenNSwag.AspNetCore还是FastEndpoints,核心都是三步——注册 OpenAPI 生成、把文档以/openapi/{documentName}.json形式暴露、再调用app.MapScalarApiReference()挂载 UI。例如 Swashbuckle 场景:

builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); if (app.Environment.IsDevelopment()) { app.MapSwagger("/openapi/{documentName}.json"); app.MapScalarApiReference(); }

仓库中的可运行示例见 playground/Scalar.AspNetCore.Playground/Program.cs,它在一个最小 API 项目中同时演示了 OpenAPI 注册、API Key 认证、自定义主题(WithTheme(ScalarTheme.Mars))、默认 HTTP 客户端(WithDefaultHttpClient(ScalarTarget.CSharp, ScalarClient.HttpClient))以及多个 Scalar 端点的挂载方式。

底层原理:Scalar 如何快速生成你的 API 文档

"怎么这么快就出来一份文档?"当你把 Scalar 加入 ASP.NET API 时,包内部实际完成了三件事(这也是官方博客明确描述的实现流程):

  1. 配置 Scalar 选项:包括 OpenAPI 文档的位置,以及标题、主题、认证等自定义项;
  2. 映射一个 GET 端点:在你的 API 上挂载一个返回页面的端点;
  3. 加载文档与配置:在该页面上,利用你的 OpenAPI 文档和 Scalar 配置,从 CDN 加载 Scalar 的 API 文档前端资源。

从源码层面看,核心实现在 src/Scalar.AspNetCore/Extensions/ScalarEndpointRouteBuilderExtensions.cs:

  • 默认端点前缀DefaultEndpointPrefix = "/scalar"(源码第 17 行),MapScalarApiReference()无参重载即挂载在该前缀下(第 32-33 行);
  • 文档名路由参数:内部映射的是MapGet("/{documentName?}", ...)(第 185 行),因此浏览器中既可以访问/scalar,也可以带文档名访问/scalar/v1;当路由参数提供了文档名时,会清空现有文档列表并只加入该文档(第 196-200 行),无文档名时则回退到默认文档名v1(第 202-205 行);
  • 尾斜杠重定向:访问/scalar会自动 301 重定向到/scalar/(第 187 行、第 265-280 行的ShouldRedirectToTrailingSlash);
  • 内嵌静态资源:包内嵌了scalar.jsscalar.aspnetcore.jsfavicon.svg三个静态资源(第 18-20 行),通过MapStaticAssetsEndpoints直接提供,支持 gzip 与 ETag 协商(第 237-263 行);
  • 页面生成:最终通过ScalarHtmlBuilder.BuildIndexHtml(...)返回text/html内容(第 219-221 行)。

这些行为都有对应的自动化测试背书,例如 tests/Scalar.AspNetCore.Tests/ScalarEndpointTests.cs 中的MapScalarApiReference_ShouldReturnIndex_WhenRequested(第 18-55 行)验证请求/scalar返回 200 及预期的 HTML 结构,MapScalarApiReference_ShouldRedirectToTrailingSlash_WhenRequestedWithoutTrailingSlash(第 57 行起)验证尾斜杠重定向逻辑。

深入配置:MapScalarApiReference 的重载与流式 API

MapScalarApiReference提供了多组重载(源码见同一文件的第 32-224 行),覆盖从最简到最复杂的定制场景:

重载形式用途
MapScalarApiReference()默认/scalar路由
MapScalarApiReference("/api-docs")自定义路由前缀
MapScalarApiReference(options => ...)同步配置选项
MapScalarApiReference(async options => ...)异步配置选项
MapScalarApiReference((options, httpContext) => ...)基于HttpContext动态配置
MapScalarApiReference("/docs", (options, httpContext) => ...)自定义路由 + 动态配置

注意:自定义路由前缀中不允许包含{documentName}占位符,否则会抛出ArgumentException(源码第 174-178 行),该占位符是框架内部保留给文档名路由参数的。

官方集成文档中还给出了常见的流式配置组合,一行链式调用即可完成多项界面定制:

app.MapScalarApiReference(options => { options.WithTitle("E-Commerce API") .WithClassicLayout() .ForceDarkMode() .HideSearch() .ShowOperationId() .ExpandAllTags() .SortTagsAlphabetically() .SortOperationsByMethod() .PreserveSchemaPropertyOrder() .WithProxy("https://api-gateway.company.com") .AddServer("https://api.company.com", "Production") .AddServer("https://staging-api.company.com", "Staging"); });

其中:

  • OpenAPI 文档路由WithOpenApiRoutePattern可自定义文档位置,既支持本地路径("/api-spec/{documentName}.json"),也支持外部 URL;
  • 自定义 JS 配置WithJavaScriptConfiguration("/scalar/config.js")可挂载一个导出默认对象的 ES 模块,实现自定义操作 slug、文档切换钩子等扩展(需通过app.MapStaticAssets()app.UseStaticFiles()暴露静态目录);
  • 资源加载:默认加载本地内嵌资源,WithBundleUrl可切换到 CDN(如https://cdn.jsdelivr.net/npm/@scalar/api-reference),DisableDefaultFonts()可关闭默认字体加载;
  • 依赖注入:也支持builder.Services.Configure<ScalarOptions>(options => options.Title = "My API")方式配置,且MapScalarApiReference内的配置优先于 DI 配置。

多版本 API:一份应用、多份文档、一个版本选择器

当 API 存在多个版本时,需要为每个版本生成独立的 OpenAPI 文档。Scalar.AspNetCore的文档详见 integrations/dotnet/aspnetcore/docs/multiple-openapi-documents.md,核心思路是:先用AddOpenApi(version, ...)按版本号分别注册文档生成器,再通过AddDocument/AddDocuments把各版本注册给 Scalar。

string[] versions = ["v1", "v2"]; foreach (var version in versions) { builder.Services.AddOpenApi(version, options => { options.AddDocumentTransformer((document, context, _) => { var descriptionProvider = context.ApplicationServices.GetRequiredService<IApiVersionDescriptionProvider>(); var versionDescription = descriptionProvider.ApiVersionDescriptions.FirstOrDefault(x => x.GroupName == version); document.Info.Version = versionDescription?.ApiVersion.ToString(); return Task.CompletedTask; }); options.AddOperationTransformer((operation, context, _) => { var apiDescription = context.Description; operation.Deprecated = apiDescription.IsDeprecated(); return Task.CompletedTask; }); }); }

随后在挂载 Scalar 时注册多份文档:

app.MapScalarApiReference(options => { // 默认路由模式:/openapi/{documentName}.json,只需文档名即可 options.AddDocument("v1"); // 只指定 routePattern options.AddDocument("v2", routePattern: "/api-docs/{documentName}/spec.json"); // 全部参数指定(名称、标题、路由模式) options.AddDocument("v3", "Version 3.0", "/api-documentation/v3.json"); // 外部文档 options.AddDocument("external", routePattern: "https://api.example.com/v1/openapi.json"); });

也可以批量注册:options.AddDocuments(versions)或传入ScalarDocument对象数组。routePattern支持{documentName}占位符,会被实际文档名替换;isDefault参数用于指定默认选中的文档(未指定时默认取列表第一份)。配置完成后,Scalar 界面会显示版本选择器,用户可自由切换不同版本及对应文档。

一个小提示:文档名会原样透传给 OpenAPI 生成器并保留大小写,因此建议统一使用小写命名(如"v1")以避免大小写敏感问题。

认证集成:从 OpenAPI 文档到可调试的认证预填

Scalar.AspNetCore依赖 OpenAPI 文档中的安全方案(security schemes)来决定是否展示认证选项——仅仅把认证方案注册进 DI 容器并不会自动出现在 OpenAPI 文档里。要让文档包含认证方案,需要实现OpenApiDocumentTransformer,详见 integrations/dotnet/aspnetcore/docs/authentication.md。

以 Bearer 认证为例,先在 DI 中注册认证方案:

builder.Services.AddAuthentication().AddJwtBearer(options => { options.Authority = "http://localhost:8080/realms/master/.well-known/openid-configuration"; });

再通过AddDocumentTransformer把安全方案写入 OpenAPI 文档:

options.AddDocumentTransformer((document, _, _) => { var securityScheme = new OpenApiSecurityScheme { Type = SecuritySchemeType.Http, In = ParameterLocation.Header, Scheme = "bearer" }; document.Components ??= new OpenApiComponents(); document.Components.SecuritySchemes.Add(JwtBearerDefaults.AuthenticationScheme, securityScheme); return Task.CompletedTask; });

可选地,还可以在 transformer 中追加document.SecurityRequirements全局安全要求,让该方案作用于全部操作。随后在 Scalar 端预选并预填认证信息:

app.MapScalarApiReference(options => options .AddPreferredSecuritySchemes("BearerAuth") .AddHttpAuthentication("BearerAuth", auth => { auth.Token = "ey..."; }) .WithPersistentAuthentication() // 可选:页面刷新后持久化认证状态 );

类似的配置覆盖 HTTP Basic、API Key、OAuth2(授权码、客户端凭证、隐式、密码四种流程),并支持为同一方案配置多种 OAuth 流程、覆盖 OpenAPI 文档中的TokenUrl/AuthorizationUrl/RedirectUri、以及通过AddDefaultScopes统一设置默认 scope。所有 OAuth2 便捷方法最终都收敛到核心方法AddOAuth2Authentication上。

[!WARNING] 预填的认证凭据会暴露在浏览器端,切勿在生产环境使用该特性,仅适用于开发与测试阶段。

用 OpenAPI 扩展增强文档:稳定性、Badge、代码示例

除了基础配置,本仓库还提供Scalar.AspNetCore.Microsoft(面向Microsoft.AspNetCore.OpenApi)与Scalar.AspNetCore.Swashbuckle(面向 Swashbuckle)两个伴生包,通过特性与扩展方法给 OpenAPI 文档注入额外元数据,详见 documentation/integrations/aspnetcore/openapi-extensions.md。对应源码分别在 integrations/dotnet/aspnetcore/src/Scalar.AspNetCore.Microsoft 与 integrations/dotnet/aspnetcore/src/Scalar.AspNetCore.Swashbuckle,两包均配有独立测试工程。

注册方式(二选一):

// Microsoft.AspNetCore.OpenApi builder.Services.AddOpenApi(options => options.AddScalarTransformers()); // Swashbuckle.AspNetCore.SwaggerGen builder.Services.AddSwaggerGen(options => options.AddScalarFilters());

之后即可用声明式 API 增强文档:

  • 稳定性标记.Stable()/.Experimental()/.Deprecated()(Minimal API)或[Stability(Stability.Stable)](控制器),帮助使用者判断生产就绪度;
  • 从文档中排除端点.ExcludeFromApiReference()让内部端点不出现在 API 参考界面(端点仍可正常访问);
  • 自定义代码示例.CodeSample(code, ScalarTarget.JavaScript, "Create Order")为端点添加多种语言的请求示例;
  • Badge 徽标.WithBadge("Alpha"),支持Before/After位置参数与任意 CSS 颜色值。

从文档到客户端:终结"文档缺失"闭环

.NET 9 内置 OpenAPI 生成 + Scalar.AspNetCore的组合,把过去"写文档、生成文档、更新文档、发布文档"四步手工流程压缩成了两行代码。更关键的是,Scalar 并不止步于展示文档——同一份 OpenAPI 文档会无缝转化为功能完整的 API 客户端,开发者可以直接在文档页面上对端点发起请求、调试响应。这正是 Scalar 构建一流 API 开发者体验的核心目标:文档即接口,接口即可测试

如果你希望进一步探索,本仓库还提供以下资源:

  • 完整接入指南:documentation/integrations/aspnetcore/integration.md
  • 可运行的示例工程:integrations/dotnet/aspnetcore/playground/Scalar.AspNetCore.Playground/Program.cs
  • 核心端点实现源码:integrations/dotnet/aspnetcore/src/Scalar.AspNetCore/Extensions/ScalarEndpointRouteBuilderExtensions.cs
  • 自动化测试(验证端点行为、重定向与 HTML 输出):integrations/dotnet/aspnetcore/tests/Scalar.AspNetCore.Tests/ScalarEndpointTests.cs
  • 高级文档(认证、多文档、子路径部署等):integrations/dotnet/aspnetcore/docs

适用前提说明:AddOpenApi/MapOpenApi为 .NET 9 内置能力;若项目仍在使用 Swashbuckle 或 NSwag 等其他生成器,Scalar.AspNetCore同样兼容,只需按上文对应的生成器接法配置即可。

【免费下载链接】scalarScalar is an open-source API platform: 🌐 Modern REST API Client 📖 Beautiful API References ✨ 1st-Class OpenAPI/Swagger Support项目地址: https://gitcode.com/GitHub_Trending/sc/scalar

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/14 14:01:38

opencode技能加载全挂?根因竟是缺失ripgrep二进制

最近我在折腾 opencode 的技能&#xff08;Skills&#xff09;功能时&#xff0c;碰到一个特别诡异的故障&#xff1a;技能列表加载全挂&#xff0c;一个都出不来&#xff0c;报错信息翻来覆去就一句话。排查了大半天&#xff0c;最后才发现根因居然是 opencode 压根没想去用系…

作者头像 李华
网站建设 2026/9/14 14:01:21

美团小程序mtgsig安全机制与开发实践详解

1. 美团小程序mtgsig安全机制解析 mtgsig是美团小程序中用于接口请求签名验证的核心安全参数&#xff0c;其作用类似于Web开发中的CSRF Token或API签名机制。这个参数通过特定算法生成&#xff0c;与服务端验证逻辑相匹配&#xff0c;主要用于防止未经授权的请求调用和接口滥用…

作者头像 李华
网站建设 2026/9/14 14:01:07

PyTorch自定义算子开发指南:从Python到CUDA

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/14 14:00:42

MFC中使用ChartCtrl绘制曲线图:Demo解析与工程实践

简介&#xff1a;一份面向MFC开发者的ChartCtrl图表控件演示工程&#xff0c;演示如何在Windows桌面程序中集成第三方图表插件并绘制高质量曲线。资源以源码形式提供&#xff0c;共58个文件&#xff0c;其中31个头文件、23个实现文件与4个内联文件分别对应控件接口声明、核心功…

作者头像 李华