news 2026/9/15 19:33:13

Scalar.AspNetCore.Swashbuckle 深度指南:用 OpenAPI Filters 为 Swashbuckle 文档注入 Scalar 扩展

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Scalar.AspNetCore.Swashbuckle 深度指南:用 OpenAPI Filters 为 Swashbuckle 文档注入 Scalar 扩展

Scalar.AspNetCore.Swashbuckle 深度指南:用 OpenAPI Filters 为 Swashbuckle 文档注入 Scalar 扩展

【免费下载链接】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.AspNetCore.Swashbuckle是 Scalar 开源 API 平台中面向 .NET 生态的官方集成包,它的核心职责是为Swashbuckle.AspNetCore.SwaggerGen提供一组 OpenAPI Filters,把 Scalar 特有的扩展(稳定性标记、徽章、代码示例、端点隐藏)写入你生成的 OpenAPI 文档中。读完本文,你将掌握该包的安装与注册方式、全部扩展能力的用法(Minimal API 与 Controller 两种写法)、底层过滤器的实现原理,以及如何用仓库内现成的测试用例验证输出结果。

包定位:在不改动业务代码的前提下"增强" OpenAPI 文档

Scalar 本身是一个开源的 API 平台,既提供现代化的 REST API 客户端,也提供漂亮的 API References 界面,并对 OpenAPI/Swagger 有一流的支持。当你在 ASP.NET Core 应用里使用 Swashbuckle 生成 OpenAPI 文档、再用 Scalar.AspNetCore 渲染 Scalar 的 API Reference 界面时,往往会希望文档中携带更多面向阅读者的语义信息——比如某个接口是 experimental 还是 stable、某个接口要打上 "New" 或 "Beta" 徽章、某个内部接口不要出现在参考文档里。

这些需求正是Scalar.AspNetCore.Swashbuckle包的用武之地。其 README 对包定位的描述非常直接:

TheScalar.AspNetCore.Swashbucklepackage provides OpenAPI filters forSwashbuckle.AspNetCore.SwaggerGenthat enable Scalar-specific extensions in your OpenAPI document.

也就是说,该包不改变你的 API 实现,而是通过 Swashbuckle 的 Filter 机制,在文档生成阶段把 Scalar 扩展写入 OpenAPI 文档。从 Scalar.AspNetCore.Swashbuckle.csproj 可以看到它的依赖面很干净:Microsoft.OpenApiSwashbuckle.AspNetCore.SwaggerGen,以及项目内引用Scalar.AspNetCore(扩展属性与序列化辅助都来自该包)。目标框架为net8.0;net9.0;net10.0,适用于当前的 .NET 8/9/10 应用。

安装与注册:一条扩展方法接入全部 Filters

首先通过 NuGet 安装包(对应官方文档 openapi-extensions.md 中的说明):

dotnet add package Scalar.AspNetCore.Swashbuckle

然后在 OpenAPI 注册阶段调用AddScalarFilters()扩展方法:

using Scalar.AspNetCore; var builder = WebApplication.CreateBuilder(args); // Swashbuckle.AspNetCore.SwaggerGen builder.Services.AddSwaggerGen(options => options.AddScalarFilters()); var app = builder.Build(); app.MapOpenApi(); // 或 app.UseSwagger() 等 Swashbuckle 的文档暴露方式 app.Run();

AddScalarFilters的实现位于 SwaggerGenOptionsExtensions.cs,一次调用共注册了 1 个 DocumentFilter 和 4 个 OperationFilter:

public static SwaggerGenOptions AddScalarFilters(this SwaggerGenOptions options) { options.DocumentFilter<ExcludeFromApiReferenceDocumentFilter>(); options.OperationFilter<ExcludeFromApiReferenceOperationFilter>(); options.OperationFilter<StabilityOpenApiOperationFilter>(); options.OperationFilter<CodeSampleOperationFilter>(); options.OperationFilter<BadgeOperationFilter>(); return options; }

各过滤器与对应扩展键的对应关系如下:

过滤器类型写入的扩展键作用
StabilityOpenApiOperationFilterOperationFilterx-scalar-stability标记接口稳定性(stable / experimental / deprecated)
BadgeOperationFilterOperationFilterx-badges为操作添加视觉徽章
CodeSampleOperationFilterOperationFilterx-codeSamples为操作添加自定义代码示例
ExcludeFromApiReferenceOperationFilterOperationFilterx-scalar-ignore标记单个操作不出现在 API Reference
ExcludeFromApiReferenceDocumentFilterDocumentFilterx-scalar-ignore(提升到 tag 级)整组隐藏某个 tag 下的全部操作

这些扩展键的常量定义集中在 ExtensionKeys.cs(ScalarIgnoreScalarStabilityCodeSamplesBadges),并通过 JsonSerializerHelper.cs 中的源生成序列化上下文,以 camelCase 命名、忽略 null 值的方式写入 JSON。

标记 API 稳定性:x-scalar-stability

稳定性信息帮助使用者判断一个接口是否适合投入生产。Scalar 定义了三个稳定性级别(见 Stability.cs 与官方文档):

  • Stable:生产就绪的 API,序列化为"stable"
  • Experimental:可能随时变更、不建议生产使用的 API,序列化为"experimental"
  • Deprecated:将在未来版本移除的 API,序列化为"deprecated"

Minimal API 写法

app.MapGet("/products", GetProducts).Stable(); app.MapGet("/beta-features", GetBetaFeatures).Experimental(); app.MapGet("/legacy-endpoint", GetLegacyData).Deprecated();

Controller 写法

[HttpGet] [Stability(Stability.Stable)] public IActionResult GetProducts() => Ok();

Minimal API 一侧的Stable()/Experimental()/Deprecated()扩展方法定义在 EndpointConventionBuilderExtensions.cs,它们内部都通过WithStability往 EndpointMetadata 中写入StabilityAttribute(定义见 StabilityAttribute.cs)。

底层过滤器 StabilityOpenApiOperationFilter.cs 的读取逻辑值得注意:

// We use LastOrDefault because this allows a specific endpoint to override the stability var stabilityAttribute = context.ApiDescription.ActionDescriptor.EndpointMetadata .OfType<StabilityAttribute>() .LastOrDefault();

也就是说,当你在分组(MapGroup)级别标记了稳定性、又在具体端点上再次标记时,LastOrDefault保证端点级标记覆盖分组级标记。命中后过滤器把稳定性值序列化为x-scalar-stability扩展:

{ "x-scalar-stability": "experimental" }

这一输出被 StabilityFilterTests.cs 以 JSON 全量比对的方式验证,测试覆盖了stableexperimentaldeprecated三种取值。

从 API Reference 中隐藏端点:x-scalar-ignore

有些内部端点你希望保留在 OpenAPI 文档(供工具调用)中,但不想让它在 Scalar 的 API Reference 界面里出现。ExcludeFromApiReference正好解决这个需求,且端点仍然可以正常访问。

Minimal API 写法

app.MapGet("/internal/metrics", GetMetrics).ExcludeFromApiReference();

Controller 写法

[HttpGet] [ExcludeFromApiReference] public IActionResult GetInternalMetrics() => Ok();

隐藏逻辑由两个过滤器协作完成:

  1. ExcludeFromApiReferenceOperationFilter.cs 是 OperationFilter,检测到ExcludeFromApiReferenceAttribute后给该操作加上x-scalar-ignore: true
  2. ExcludeFromApiReferenceDocumentFilter.cs 是 DocumentFilter,它先把文档中所有操作按 tag 分组,然后找出"该 tag 下全部操作都带x-scalar-ignore"的 tag,把忽略标记提升到 tag 级别,并移除操作上的冗余标记:
var tagsToExclude = tagOperations.Where(kvp => kvp.Value.All(operation => operation.Extensions is not null && operation.Extensions.ContainsKey(ScalarIgnore)));

这样处理的好处是:Scalar 渲染时只需要识别 tag 级标记即可整组隐藏,文档结构也更干净。注意官方文档明确提示——被隐藏的端点仍然可以通过 API 访问,只是不会出现在 API Reference 界面中。

添加自定义代码示例:x-codeSamples

默认情况下,Scalar 的 API Reference 会为每个操作自动生成多种语言的代码示例。如果你希望针对某个端点提供手写示例(例如展示特定的鉴权头、特定的调用方式),可以用CodeSample覆盖或补充。

Minimal API 写法

app.MapPost("/orders", CreateOrder) .CodeSample("fetch('/orders', { method: 'POST', body: JSON.stringify(order) })", ScalarTarget.JavaScript, "Create Order") .CodeSample("curl -X POST /orders -d @order.json", ScalarTarget.Shell, "Create with cURL");

Controller 写法

[HttpGet] [CodeSample("fetch('/products').then(r => r.json())", ScalarTarget.JavaScript)] public IActionResult GetProducts() => Ok();

CodeSampleAttribute(见 CodeSampleAttribute.cs)有三个构造参数:sample(示例代码内容)、languageScalarTarget枚举,如ScalarTarget.CSharpScalarTarget.JavaScriptScalarTarget.Shell)、label(可选标签),并且AllowMultiple = true,一个端点可以挂多个示例。Minimal API 侧对应的扩展方法是 EndpointConventionBuilderExtensions.cs 中的CodeSample

CodeSampleOperationFilter.cs 会把端点元数据中的所有CodeSampleAttribute批量收集为CodeSample数据模型,写入x-codeSamples扩展:

{ "x-codeSamples": [ { "source": "fetch('/orders', { method: 'POST', body: JSON.stringify(order) })", "label": "Create Order", "language": "javascript" } ] }

添加视觉徽章:x-badges

徽章用于在 API Reference 界面上给操作附加醒目的视觉标识,比如 "New"、"Beta"、"Internal"。每个操作可以挂多个徽章,并分别配置位置与颜色。

Minimal API 写法

app.MapGet("/alpha-feature", GetAlphaFeature) .WithBadge("Alpha") .WithBadge("Beta", BadgePosition.Before) .WithBadge("Internal", BadgePosition.After, "#ff6b35"); app.MapPost("/orders", CreateOrder) .WithBadge("New", color: "#28a745") .WithBadge("Premium", BadgePosition.Before, "#ffc107");

Controller 写法

[HttpGet] [Badge("New")] [Badge("V2", BadgePosition.After, "#007bff")] public IActionResult GetExperimentalFeature() => Ok();

徽章的可配置项(官方文档 openapi-extensions.md 与 BadgeAttribute.cs 均有说明):

参数必填说明
name徽章上显示的文本
position徽章相对操作标题的位置,BadgePosition.After(默认)/BadgePosition.Before
color徽章颜色,支持任意 CSS 颜色格式(hex、rgb、颜色关键字等)

BadgePosition枚举定义在 BadgePosition.cs,序列化时分别输出为"after"/"before"

BadgeOperationFilter.cs 会收集端点上的全部BadgeAttribute(同样AllowMultiple = true)写入x-badges扩展。仓库测试 BadgeFilterTests.cs 给出了非常直观的期望输出——当分组级挂了 "Alpha" 徽章、端点级又挂了 "Beta"(before)、"Gamma"(after + 颜色)、"Delta"(仅颜色)时,生成的 JSON 为:

"x-badges": [ { "name": "Alpha" }, { "name": "Beta", "position": "before" }, { "name": "Gamma", "position": "after", "color": "#ffcc00" }, { "name": "Delta", "color": "#00ff00" } ]

可见分组级徽章会自动继承到组内每个操作上,这也是为什么一个操作可能同时包含来自分组和自身两部分的徽章。

已弃用端点的处理

除了x-scalar-stability: "deprecated"这种标记方式,仓库中还提供了一个专门处理DeprecatedAttribute的过滤器 DeprecatedEndpointFilter.cs。它读取Scalar.AspNetCore.Attributes.DeprecatedAttribute(见 DeprecatedAttribute.cs,可传入可选参数reason说明弃用原因或新接口指引),然后:

  1. operation.Deprecated置为true(对应 OpenAPI 标准的deprecated字段);
  2. 若提供了reason,则把它追加到操作的Description中。

需要说明的是:从源码结构看,AddScalarFilters当前并未注册该过滤器,仓库中已注册的是稳定性、徽章、代码示例和隐藏端点这五个过滤器。如果你希望启用"原因描述"式的弃用标注,可以按需自行通过options.OperationFilter<...>()注册,或优先使用[Stability(Stability.Deprecated)]/.Deprecated()的稳定性路径。

底层实现剖析:一套统一的元数据读取模式

把五个过滤器放在一起看,会发现它们遵循完全一致的实现模式,这也是本包易于理解和扩展的原因:

  1. 从 EndpointMetadata 读取特性:所有过滤器都通过context.ApiDescription.ActionDescriptor.EndpointMetadata.OfType<TAttribute>()从端点元数据中取出对应特性——Minimal API 的.WithBadge(...)/.CodeSample(...)扩展方法最终调用builder.WithMetadata(new XxxAttribute(...))(见 EndpointConventionBuilderExtensions.cs),Controller 则直接使用[Xxx]特性,两条路径殊途同归,最终都落在 EndpointMetadata 上;
  2. 按需初始化扩展集合operation.Extensions ??= new Dictionary<string, IOpenApiExtension>();,确保不覆盖已有扩展;
  3. 幂等写入:使用Extensions.TryAdd(...)避免重复注入同一个扩展键;
  4. 统一序列化:JsonSerializerHelper.cs 使用System.Text.Json的源生成上下文(ScalarExtensionsSerializerContext)序列化CodeSampleBadgeStability等模型,并统一配置 camelCase 命名与 null 值忽略,保证输出的扩展 JSON 风格一致。

这种"特性(Attribute)声明 + Filter 消费"的设计,让所有扩展能力都可以同时工作在 Minimal API、Controller 与最小化 API 的各种托管模型上,且不需要修改任何业务实现代码。

测试与验证:仓库如何保证扩展输出正确

Scalar.AspNetCore.Swashbuckle的每个过滤器都有对应的集成测试,位于 integrations/dotnet/aspnetcore/tests/Scalar.AspNetCore.Swashbuckle.Tests,包括:

  • BadgeFilterTests.cs:验证x-badges的完整 JSON 结构;
  • StabilityFilterTests.cs:验证x-scalar-stability三种取值;
  • CodeSampleFilterTests.cs:验证x-codeSamples的注入;
  • ExcludeFromApiReferenceFilterTests.cs:验证x-scalar-ignore的隐藏逻辑。

测试的写法很有参考价值:它们通过WebApplicationFactory+ConfigureTestServices注册AddScalarFilters,动态搭建路由(含MapSwaggerMapGroup、端点级与分组级的特性组合),再请求生成的 OpenAPI JSON 与期望输出做全量匹配。如果你在自己的项目中接入本包,完全可以把这些测试当作行为契约,快速确认扩展输出的精确格式。

快速上手参考

一个完整的 Minimal API 接入示例(综合了分组级与端点级扩展,参考仓库 playground 的 BookEndpoints.cs 与 Program.cs):

using Scalar.AspNetCore; var builder = WebApplication.CreateBuilder(args); builder.Services.AddSwaggerGen(options => options.AddScalarFilters()); builder.Services.AddOpenApi(); var app = builder.Build(); app.MapOpenApi(); app.MapScalarApiReference(); // 挂载 Scalar API Reference 界面 var books = app.MapGroup("/books").WithTags("bookstore"); books.MapGet("/", () => Results.Ok()).Stable(); books.MapGet("/{id}", (Guid id) => Results.Ok()).Experimental() .WithBadge("Beta", BadgePosition.Before, "#ffcc00"); books.MapDelete("/{id}", (Guid id) => Results.NoContent()) .WithBadge("Caution", BadgePosition.Before, "#ffc2c2"); app.Run();

至此,你的 Swashbuckle 文档中就会自动携带x-scalar-stabilityx-badgesx-codeSamplesx-scalar-ignore等 Scalar 扩展,Scalar API Reference 界面将据此渲染出带稳定性标识、徽章、自定义代码示例且过滤掉内部端点的文档体验。想要进一步查阅各扩展的完整能力与属性说明,可以对照 openapi-extensions.md 文档,并结合本文提到的过滤器源码逐一验证。

【免费下载链接】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/15 19:33:12

three.js r137 离线包全解析:从 importmap 到数字孪生场景搭建

简介&#xff1a;three.js-r137.zip 是为前端开发者准备的 three.js r137 版本资料集&#xff0c;聚焦 WebGL 3D 渲染技术&#xff0c;帮助读者快速掌握在浏览器中构建三维场景的方法。压缩包共 2000 个文件&#xff0c;约 306.64MB&#xff0c;以 JS 源码和 HTML 示例为主&…

作者头像 李华
网站建设 2026/9/15 19:27:11

数字人直播实战指南:不出镜不露脸的AI驱动方案

1. 为什么“不出镜不露脸”正在成为直播新刚需最近帮三个做知识付费的朋友搭数字人直播系统&#xff0c;他们提的需求惊人地一致&#xff1a;“能不能让我人不在镜头前&#xff0c;但直播间看起来还是我在讲&#xff1f;”不是偷懒&#xff0c;而是现实逼出来的选择——有人刚做…

作者头像 李华
网站建设 2026/9/15 19:26:42

大语言模型技术进展与应用实践解析

1. 大语言模型研究现状概述过去两年间&#xff0c;大语言模型&#xff08;LLM&#xff09;领域经历了从技术突破到产业落地的快速演进。作为从业者&#xff0c;我观察到这个领域正呈现出"基础模型规模化"与"垂直场景精细化"并行的双轨发展态势。根据2023年…

作者头像 李华
网站建设 2026/9/15 19:26:34

产业分析实战:技术评估与市场生态位扫描

1. 产业分析的价值与挑战2008年金融危机期间&#xff0c;我亲眼见证了一家传统制造企业因为误判产业趋势而濒临破产。当时他们投入巨资扩建的产线&#xff0c;在危机后市场需求结构变化中完全失去了竞争力。这件事让我深刻认识到&#xff1a;产业分析不是学者书斋里的理论游戏&…

作者头像 李华