写作与公文
为 .NET 10 API 配置内置 OpenAPI…
适用于 .NET 10 应用设置 API 文档、定制 OpenAPI 输出、添加安全方案或从规范生成客户端时。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 38/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 55/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 60/100 ×0.15
适用场景
为 .NET 10 API 配置内置 O特定
解决思路
- 使用内置 Microsoft.AspNetCore.OpenApi,而不是 Swashbuckle;.NET 10 已内置官方 OpenAPI 方案,Swashbuckle 从 .NET 9 模板移除且不再推荐。
- 使用 TypedResults(如 `TypedResults.Ok<T>()`)驱动响应 schema;`Results.Ok()` 不会贡献 OpenAPI schema。
- 使用 Document、Operation、Schema transformers 处理安全方案、全局响应和 schema 定制,而不是用绕过方案。
- 在每个 endpoint 上使用 `.WithName()`、`.WithSummary()`、`.WithTags()`,让元数据进入 OpenAPI spec 和客户端生成器。
- 基本设置:`var builder = WebApplication.CreateBuilder(args); builder.Services.AddOpenApi(); var app = builder.Build(); if (app.Environment.IsDevelopment()) { app.MapOpenApi(); }`,默认服务于 `/openapi/v1.json`。
- Endpoint 元数据:`group.MapPost("/", CreateOrder).WithName("CreateOrder").WithSummary("Create a new order").WithDescription("Creates a new order for the specified customer.").Produces<OrderResponse>(StatusCodes.Status201Created).ProducesValidationProblem().ProducesProblem(StatusCodes.Status500InternalServerError);`
- 使用 TypedResults 时响应元数据自动推断:`static async Task<Results<Created<OrderResponse>, ValidationProblem>> CreateOrder(...)` 返回 `TypedResults.Created(...)` 或 `TypedResults.ValidationProblem(...)`。
- Bearer Token 安全方案:在 `builder.Services.AddOpenApi(options => { options.AddDocumentTransformer<BearerSecuritySchemeTransformer>(); });` 中注册 document transformer。
- 实现 BearerSecuritySchemeTransformer:继承 `IOpenApiDocumentTransformer`,注入 `IAuthenticationSchemeProvider`,在 `TransformAsync` 中 `await authSchemeProvider.GetAllSchemesAsync()`。
- 在 BearerSecuritySchemeTransformer 中,如果没有名为 Bearer 的 scheme 则 `return`;否则设置 `document.Components ??= new OpenApiComponents();` 并配置 `document.Components.SecuritySchemes["Bearer"] = new OpenApiSecurityScheme { Type = SecuritySchemeType.Http, Scheme = "bearer", BearerFormat = "JWT", In = ParameterLocation.Header };`。
- 在 BearerSecuritySchemeTransformer 中,遍历 `document.Paths.Values.SelectMany(p => p.Operations)`,对每个 operation 初始化 `operation.Value.Security ??= [];` 并 `Add(new OpenApiSecurityRequirement { [new OpenApiSecuritySchemeReference("Bearer", document)] = [] });`。
- Document Info Transformer:`options.AddDocumentTransformer((document, context, ct) => { document.Info = new() { Title = "Checkout API", Version = "v1", Description = "API for processing orders and payments." }; return Task.CompletedTask; });`
- 多个 OpenAPI 文档:使用 `builder.Services.AddOpenApi("v1");` 和 `builder.Services.AddOpenApi("internal", options => { options.AddDocumentTransformer<BearerSecuritySchemeTransformer>(); });`。
- 多个文档时,endpoint 用 `.WithGroupName("v1")` 或 `.WithGroupName("internal")` 选择文档;未调用 `.WithGroupName()` 的 endpoint 出现在所有文档中。
- XML 文档注释:在项目文件中启用 `<PropertyGroup><GenerateDocumentationFile>true</GenerateDocumentationFile></PropertyGroup>`,source generator 会自动提取 `<summary>`、`<param>`、`<response>` 标签。
- 在命名方法上写 XML 注释,例如 `/// <summary>Retrieves a project board by ID.</summary> /// <param name="id">The project board ID.</param> /// <response code="200">Returns the project board.</response> /// <response code="404">Board not found.</response>`,并返回 `Results<Ok<Board>, NotFound>`。
- XML 注释不要写在 lambda 上:编译器不会捕获 lambda 的 XML 注释,使用命名方法。
- Schema Transformer:`options.AddSchemaTransformer((schema, context, ct) => { if (context.JsonTypeInfo.Type == typeof(decimal)) { schema.Format = "decimal"; } return Task.CompletedTask; });`
- Per-Endpoint Operation Transformer:`app.MapGet("/old", () => "deprecated").AddOpenApiOperationTransformer((operation, context, ct) => { operation.Deprecated = true; return Task.CompletedTask; });`
- Build-Time Document Generation:添加 `<PackageReference Include="Microsoft.Extensions.ApiDescription.Server" Version="*" />`。
- Build-Time Document Generation:设置 `<PropertyGroup><OpenApiDocumentsDirectory>.</OpenApiDocumentsDirectory></PropertyGroup>`,构建期间会在输出目录生成规范文件。
- YAML Endpoint:`app.MapOpenApi("/openapi/{documentName}.yaml");`
- 不要在新项目中使用 Swashbuckle:不要使用 `builder.Services.AddSwaggerGen(); app.UseSwagger(); app.UseSwaggerUI();`,改用 `builder.Services.AddOpenApi(); app.MapOpenApi();`。
- 不要在 .NET 10 中使用 `WithOpenApi()`(会生成 ASPDEPR002 警告);改用 `.AddOpenApiOperationTransformer(...)`。
- 不要使用无类型 Results:不要用 `Results.Ok(order)` / `Results.NotFound()`;改用 `static async Task<Results<Ok<Order>, NotFound>>` 和 `TypedResults.Ok` / `TypedResults.NotFound`。
- 不要跳过 endpoint 的 WithName:不要只写 `group.MapGet("/{id:guid}", GetOrder);`,要写 `group.MapGet("/{id:guid}", GetOrder).WithName("GetOrder");`,否则客户端生成器方法名很差。
- 不要在 .NET 10 中使用 OpenApiAny:不要写 `schema.Example = new OpenApiString("2025-01-01");`,改用 `schema.Example = JsonValue.Create("2025-01-01");`(来自 System.Text.Json.Nodes)。
- 决策建议:新 API 项目使用 `AddOpenApi()` + `MapOpenApi()`(内置)。
- 决策建议:API 文档 UI 使用 Scalar(`MapScalarApiReference()`)。
- 决策建议:文档中的安全方案使用实现 `IOpenApiDocumentTransformer` 的 document transformer。
- 决策建议:响应文档使用 `TypedResults` 和 union return types。
- 决策建议:XML 文档集成使用 `<GenerateDocumentationFile>true</GenerateDocumentationFile>`。
- 决策建议:多 API 版本使用多个 `AddOpenApi("v1")` 调用 + `WithGroupName()`。
- 决策建议:客户端代码生成使用 Kiota(Microsoft 推荐)或 NSwag。
- 决策建议:构建时规范使用 `Microsoft.Extensions.ApiDescription.Server` 包。
- 决策建议:OpenAPI 版本使用 3.1(.NET 10 默认);如果消费者要求,则强制使用 3.0。
- 决策建议:按 endpoint 定制使用 `.AddOpenApiOperationTransformer()`。
适用边界 · 注意事项
- 项目不是 .NET 10 或无法使用 `Microsoft.AspNetCore.OpenApi` 内置支持时不适用。
- 必须继续使用 Swashbuckle 特有功能或既有 Swagger UI 迁移成本过高时不适用。
- 消费者只接受 OpenAPI 3.0 且不能处理 .NET 10 默认的 3.1 时,需先强制 3.0 或确认兼容性,否则不直接适用。