.NET 9 中使用 Scalar 替代内置的 Swagger 支持 (Swashbuckle)
- 为什么 Swagger (Swashbuckle) 被删除?
- Swagger 的替代方案:Scalar(Scalar.AspNetCore)
- 如何在 Dotnet 9 中使用它?
- 如何将 Bearer 身份验证方案添加到 Scalar ?
- 使用 Scalar 的式例
- 创建 webapi 项目
- 安装 nuget 包
- 添加文件夹目录
- 修改 Program.cs 文件
- Scalar 的配置扩展
Microsoft
已决定从.NET 9
中删除内置的Swagger
支持 (Swashbuckle
)。
为什么 Swagger (Swashbuckle) 被删除?
ASP.NET Core
团队已决定从 .NET 9
中删除内置的 Swagger
支持 (Swashbuckle
),原因如下:
- 维护问题:
Swashbuckle
项目不再由其社区所有者积极维护。问题未得到解决或解决,并且.NET 8
没有正式版本。 ASP.NET Core
的演变:自从在.NET 5
中引入Swagger
支持以来,ASP.NET Core
已经有了显著的发展。它现在内置了对描述Web API
所需的元数据的支持,从而减少了对外部工具的需求。- 专注于
OpenAPI
:该团队希望使OpenAPI
成为ASP.NET Core
中的一等公民。他们计划扩展OpenAPI
文档生成功能,而不依赖外部包。Microsoft.AspNetCore.OpenApi
- 替代工具:
Visual Studio
现在提供对.http
文件的内置支持和新的Endpoints Explorer
,从而提供探索、测试和调试API
的替代方法。 - 社区驱动的创新:通过消除默认依赖项,团队鼓励使用和开发可能更适合特定项目需求的各种
OpenAPI
工具。
Swagger 的替代方案:Scalar(Scalar.AspNetCore)
Scalar
是来自 OpenAPI/Swagger
文档的交互式 API
文档。
这个
.NET
包Scalar.AspNetCore
提供了一种基于OpenAPI/Swagger
文档呈现漂亮的API
引用的简单方法。
您可以在此处获取更多信息。
- https://github.com/scalar/scalar
- https://www.nuget.org/packages/Scalar.AspNetCore
如何在 Dotnet 9 中使用它?
1、安装 nuget 包
dotnet add package Scalar.AspNetCore
2、示例用法
using Scalar.AspNetCore;var builder = WebApplication.CreateBuilder(args);builder.Services.AddOpenApi();var app = builder.Build();if (app.Environment.IsDevelopment())
{app.MapScalarApiReference(); // scalar/v1app.MapOpenApi();
}app.MapGet("/", () => "Hello world!");app.Run();
运行应用程序时,您可以在终端节点访问 API
文档。
http://localhost:port/scalar/v1
如何将 Bearer 身份验证方案添加到 Scalar ?
- 以下是
Bearer
身份验证的示例转换器:
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.OpenApi;
using Microsoft.OpenApi.Models;namespace WebApplication1.Transformers;public sealed class BearerSecuritySchemeTransformer(IAuthenticationSchemeProvider authenticationSchemeProvider) : IOpenApiDocumentTransformer
{private readonly string _authenticationSchemeName = "Bearer";public async Task TransformAsync(OpenApiDocument document, OpenApiDocumentTransformerContext context, CancellationToken cancellationToken){var authenticationSchemes = await authenticationSchemeProvider.GetAllSchemesAsync();if (authenticationSchemes.Any(authScheme => authScheme.Name == _authenticationSchemeName)){// Add the security scheme at the document levelvar requirements = new Dictionary<string, OpenApiSecurityScheme>{[_authenticationSchemeName] = new OpenApiSecurityScheme{Type = SecuritySchemeType.Http,Scheme = _authenticationSchemeName.ToLower(), // "bearer" refers to the header name hereIn = ParameterLocation.Header,BearerFormat = "Json Web Token"}};document.Components ??= new OpenApiComponents();document.Components.SecuritySchemes = requirements;// Apply it as a requirement for all operationsforeach (var operation in document.Paths.Values.SelectMany(path => path.Operations)){operation.Value.Security.Add(new OpenApiSecurityRequirement{[new OpenApiSecurityScheme{Reference = new OpenApiReference{Id = _authenticationSchemeName,Type = ReferenceType.SecurityScheme}}] = Array.Empty<string>()});}}}
}
- 用法示例:
builder.Services.AddOpenApi(opt =>
{opt.AddDocumentTransformer<BearerSecuritySchemeTransformer>();
});
使用 Scalar 的式例
创建 webapi 项目
此处我们使用 .NET CLI
创建 ASP.NET Core Web API
项目,操作步骤如下:
1、确保已安装 .NET SDK
:
- 可以通过运行
dotnet --version
命令来检查是否已安装.NET9 SDK
。 - 如果未安装,可以从
.NET
官方网站 下载并安装。
.NET
官方网站 :
- https://dotnet.microsoft.com/zh-cn/download
- https://dotnet.microsoft.com/en-us/download
2、创建项目目录:
mkdir MyWebApi
3、创建 ASP.NET Core Web API
项目:
dotnet new webapi -n MyWebApi
4、导航到项目目录:
cd MyWebApi
5、运行项目:
dotnet run
通过上面操作,此时我们就准备好 asp.net core webapi
项目,名称为:MyWebApi
。
安装 nuget 包
使用 .net cli
安装 nuget
包文件,执行如下命令:
dotnet add package Microsoft.AspNetCore.OpenApi --version 9.0.0-rc.2.24474.3
dotnet add package Scalar.AspNetCore --version 1.2.23
添加文件夹目录
在项目中添加文件夹目录,命名为:Transformers
,并在该目录中添加 BearerSecuritySchemeTransformer.cs
文件,该文件的作用是将 Bearer 身份验证方案添加到 Scalar 中,代码演示如签名环节说明。
修改 Program.cs 文件
此处使用 MiniAPI
模式,在 Program.cs
文件中代码改造如下:
using System.Text.Json.Serialization;
using Scalar.AspNetCore;
using WebApplication1.Transformers;var builder = WebApplication.CreateSlimBuilder(args);// 配置 HTTP JSON 选项,用于自定义 JSON 序列化行为。
builder.Services.ConfigureHttpJsonOptions(options =>
{// 在类型解析链中插入自定义的 JSON 序列化上下文。options.SerializerOptions.TypeInfoResolverChain.Insert(0, AppJsonSerializerContext.Default);
});// 启用 OpenAPI 文档支持,并配置 OpenAPI 文档。
//builder.Services.AddOpenApi();
builder.Services.AddOpenApi(opt =>
{// 添加一个文档转换器,用于处理特定的安全方案(例如: Bearer Token)。opt.AddDocumentTransformer<BearerSecuritySchemeTransformer>();
});var app = builder.Build();if (app.Environment.IsDevelopment())
{// 映射 OpenAPI 文档路由。app.MapOpenApi();//app.MapScalarApiReference(); // scalar/v1// 映射自定义的 API 文档路由,并使用 Fluent API 进行配置,例如:设置标题、主题、侧边栏等。app.MapScalarApiReference(options =>{options.WithTitle("My custom API").WithTheme(ScalarTheme.Mars).WithSidebar(true).WithDefaultHttpClient(ScalarTarget.CSharp, ScalarClient.HttpClient).WithPreferredScheme("ApiKey").WithApiKeyAuthentication(x => x.Token = "my-api-key");});
}// 定义示例数据,一个包含示例待办事项的数组
var sampleTodos = new Todo[] {new(1, "Walk the dog"),new(2, "Do the dishes", DateOnly.FromDateTime(DateTime.Now)),new(3, "Do the laundry", DateOnly.FromDateTime(DateTime.Now.AddDays(1))),new(4, "Clean the bathroom"),new(5, "Clean the car", DateOnly.FromDateTime(DateTime.Now.AddDays(2)))
};// 定义 API 路由
var todosApi = app.MapGroup("/todos"); // 创建一个路由组,前缀为 /todos。
todosApi.MapGet("/", () => sampleTodos); // 映射 GET 请求到根路径,返回所有待办事项。
// 映射 GET 请求到特定 ID 的路径,返回对应的待办事项,如果找不到则返回 404 Not Found。
todosApi.MapGet("/{id}", (int id) =>sampleTodos.FirstOrDefault(a => a.Id == id) is { } todo? Results.Ok(todo): Results.NotFound());app.Run();// 定义一个记录类 Todo,包含 Id、Title、DueBy 和 IsComplete 属性。
public record Todo(int Id, string? Title, DateOnly? DueBy = null, bool IsComplete = false);// 自定义的 JSON 序列化上下文
[JsonSerializable(typeof(Todo[]))] // 标记 AppJsonSerializerContext 类,使其支持 Todo 数组的 JSON 序列化。
internal partial class AppJsonSerializerContext : JsonSerializerContext
{// 定义一个部分类 AppJsonSerializerContext,继承自 JsonSerializerContext,用于自定义 JSON 序列化行为。
}
运行项目,浏览器输入地址:
- 查看
todos
接口数据
http://localhost:5264/todos
- 查看
scalar
页面:
http://localhost:5264/scalar/v1
- /todos
点击 【test request】显示如下:
点击【send】显示如下:
Scalar 的配置扩展
在 Scalar.AspNetCore
包中,IEndpointRouteBuilder
该方法有一个可选参数,可用于自定义 Scalar UI
的行为:MapScalarApiReference
#region 程序集 Scalar.AspNetCore, Version=1.2.23.0, Culture=neutral, PublicKeyToken=null
// C:\Users\Jeffrey\.nuget\packages\scalar.aspnetcore\1.2.23\lib\net9.0\Scalar.AspNetCore.dll
// Decompiled with ICSharpCode.Decompiler 8.1.1.7464
#endregionusing System;
using System.Text.Json;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Generated;
using Microsoft.AspNetCore.Routing;
using Microsoft.AspNetCore.StaticFiles;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Options;namespace Scalar.AspNetCore;//
// 摘要:
// Extension methods for Microsoft.AspNetCore.Routing.IEndpointRouteBuilder to provide
// required endpoints.
public static class ScalarEndpointRouteBuilderExtensions
{private const string DocumentName = "{documentName}";private const string StaticAssets = "ScalarStaticAssets";internal const string ScalarJavaScriptFile = "scalar.js";//// 摘要:// Maps the Scalar API reference endpoint.//// 参数:// endpoints:// Microsoft.AspNetCore.Routing.IEndpointRouteBuilder.public static IEndpointConventionBuilder MapScalarApiReference(this IEndpointRouteBuilder endpoints){return endpoints.MapScalarApiReference(delegate{});}//// 摘要:// Maps the Scalar API reference endpoint.//// 参数:// endpoints:// Microsoft.AspNetCore.Routing.IEndpointRouteBuilder.//// configureOptions:// An action to configure the Scalar options.public static IEndpointConventionBuilder MapScalarApiReference(this IEndpointRouteBuilder endpoints, Action<ScalarOptions> configureOptions){ScalarOptions options = endpoints.ServiceProvider.GetService<IOptions<ScalarOptions>>()?.Value ?? new ScalarOptions();configureOptions(options);if (!options.EndpointPathPrefix.Contains("{documentName}")){throw new ArgumentException("'EndpointPathPrefix' must define '{documentName}'.");}bool useLocalAssets = string.IsNullOrEmpty(options.CdnUrl);string standaloneResourceUrl = (useLocalAssets ? options.EndpointPathPrefix.Replace("{documentName}", "scalar.js") : options.CdnUrl);EmbeddedFileProvider fileProvider = new EmbeddedFileProvider(typeof(ScalarEndpointRouteBuilderExtensions).Assembly, "ScalarStaticAssets");FileExtensionContentTypeProvider fileExtensionContentTypeProvider = new FileExtensionContentTypeProvider();string configuration = JsonSerializer.Serialize(options.ToScalarConfiguration(), ScalaConfigurationSerializerContext.Default.ScalarConfiguration);return endpoints.MapGet0(options.EndpointPathPrefix, (Func<string, IResult>)delegate (string documentName){if (useLocalAssets){IFileInfo fileInfo = fileProvider.GetFileInfo(documentName);if (fileInfo.Exists){string contentType;string contentType2 = (fileExtensionContentTypeProvider.TryGetContentType(documentName, out contentType) ? contentType : "application/octet-stream");return Results.Stream(fileInfo.CreateReadStream(), contentType2, null, fileInfo.LastModified);}}string value = options.Title.Replace("{documentName}", documentName);string value2 = options.OpenApiRoutePattern.Replace("{documentName}", documentName);return Results.Content($"<!doctype html>\n<html>\n<head>\n <title>{value}</title>\n <meta charset=\"utf-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n</head>\n<body>\n <script id=\"api-reference\" data-url=\"{value2}\"></script>\n <script>\n document.getElementById('api-reference').dataset.configuration = JSON.stringify({configuration})\n </script>\n <script src=\"{standaloneResourceUrl}\"></script>\n</body>\n</html>", "text/html");}).ExcludeFromDescription();}
}
MapScalarApiReference
方法,除了上面的式例中使用的 Fluent API
模式,还可以使用 Object initializer
方式:
// Object initializer
app.MapScalarApiReference(options =>
{options.Title = "My custom API";options.Theme = ScalarTheme.Mars;options.ShowSidebar = false;options.DefaultHttpClient = new(ScalarTarget.CSharp, ScalarClient.HttpClient);options.Authentication = new ScalarAuthenticationOptions{PreferredSecurityScheme = "ApiKey",ApiKey = new ApiKeyOptions{Token = "my-api-key"}};
});
有关更多可能的选项及其默认值,请查看 ScalarOptions.cs
类。
ScalarOptions.cs
类,https://github.com/scalar/scalar/blob/main/packages/scalar.aspnetcore/src/Scalar.AspNetCore/Options/ScalarOptions.cs
也可以使用 options
模式通过依赖注入来配置选项:
builder.Services.Configure<ScalarOptions>(options => options.Title = "My custom API");
// or
builder.Services.AddOptions<ScalarOptions>().BindConfiguration("Scalar");
注意:通过该方法设置的选项将优先于通过依赖关系注入设置的选项。
MapScalarApiReference