☰
ASP.NET Minimal API + OpenAPI 实战指南:构建类型安全、自带完整文档的 .NET 端点
2026/10/9 13:12:46 网站建设 项目流程

【免费下载链接】autoskills

One command. Your entire AI skill stack. Installed.

项目地址:https://gitcode.com/gh_mirrors/au/autoskills
点击查看免费下载

导读

本指南基于 autoskills 技能库中的 aspnet-minimal-api-openapi SKILL.md 展开,完整讲解如何用 ASP.NET Minimal API 编写结构清晰、类型正确、并且自带完整 OpenAPI/Swagger 文档的 HTTP 端点。你将掌握路由分组与端点过滤器、DTO 与验证、TypedResults/Results<T1,T2>类型体系,以及基于 .NET 9 内置 OpenAPI 能力(WithName、描述、文档/模式转换器)的文档定制方案,最终交付可直接复制运行的实战代码。

为什么这份技能被收录进 autoskills

在进入技术细节之前,先看这份技能在 autoskills 项目中的定位,便于理解它的适用范围。autoskills 通过扫描项目中的配置文件自动检测技术栈,在 skills-map.ts 中,aspnet-minimal-api的检测条件是在appsettings.json等配置文件中匹配到Microsoft.AspNetCore.OpenApi或Swashbuckle.AspNetCore依赖:

{ id: "aspnet-minimal-api", name: "ASP.NET Minimal API", detect: { configFiles: ["appsettings.json"], configFileContent: { scanDotNetLayout: true, patterns: ["Microsoft.AspNetCore.OpenApi", "Swashbuckle.AspNetCore"], }, }, skills: [ "github/awesome-copilot/aspnet-minimal-api-openapi", "dotnet/skills/minimal-api-file-upload", ], }

对应地,lib.ts 中的resolveConfigFileContentPaths通过scanDotNetLayout递归扫描项目中的.sln、.csproj、.fsproj文件来确定候选路径;而在 README.md 的检测矩阵中,ASP.NET Minimal API 的识别信号同样是.csproj中的Microsoft.AspNetCore.OpenApi或Swashbuckle.AspNetCore。

也就是说:只要你的 .NET 项目引用了 OpenAPI 相关包,autoskills 就会自动为你安装这份技能,用于指导 AI 助手在编写端点时遵循本文所述的规范。技能的注册信息(来源、commit、sha256 校验)记录在 skills-registry/index.json 中。

下面进入正题。

一、API 组织:让端点结构清晰可维护

Minimal API 的一大优势是端点定义集中、样板代码少,但随着端点数量增长,散落的app.MapGet()会让代码难以维护。技能文档给出了四条组织原则:

1. 使用MapGroup()分组相关端点

MapGroup()允许为一批端点共享统一的路由前缀和公共行为:

var app = builder.Build(); var todos = app.MapGroup("/api/todos") .RequireAuthorization() // 组级授权 .WithTags("Todos"); // OpenAPI 标签分组 todos.MapGet("/", GetAllTodos); todos.MapGet("/{id}", GetTodoById); todos.MapPost("/", CreateTodo); todos.MapDelete("/{id}", DeleteTodo); app.Run();

路由前缀、中间件、授权、标签等组级配置只需写一次,组内所有端点自动继承,避免在每个端点重复声明。

2. 使用端点过滤器处理横切关注点

当某些行为(如日志、校验、限流、性能统计)需要作用于多个端点时,应使用IEndpointFilter而非在业务代码里重复实现。过滤器可以注册到单个端点,也可以注册到整个路由组:

app.MapPost("/api/todos", CreateTodo) .AddEndpointFilter<ValidationFilter<TodoRequest>>(); // 或者注册到组,组内所有端点生效 var group = app.MapGroup("/api/todos").AddEndpointFilter<RequestLoggingFilter>();

过滤器位于中间件之后、端点处理器之前,是"面向端点层横切逻辑"的标准挂载点。companion 技能 aspnet-core/references/apis-minimal-and-controllers.md 也强调:"use endpoint filters when cross-cutting behavior belongs at the endpoint layer",即横切行为属于端点层时优先用端点过滤器。

3. 大型 API 拆分为独立的端点类

当单个文件无法容纳所有端点时,可以把一组相关端点提取为独立类,通过MapXxxApi()扩展方法组织:

public static class TodoEndpoints { public static RouteGroupBuilder MapTodoApi(this IEndpointRouteBuilder routes) { var group = routes.MapGroup("/api/todos"); group.MapGet("/", GetAll); group.MapGet("/{id}", GetById); group.MapPost("/", Create); return group; } } // Program.cs app.MapTodoApi();

4. 复杂 API 采用基于功能(feature)的文件夹结构

对于功能较多的 API,可按功能而非技术类型组织目录,使页面、端点、服务、验证、数据访问与测试易于追踪:

Features/ Todos/ Endpoints.cs // 端点定义 TodoRequest.cs // 请求 DTO TodoResponse.cs // 响应 DTO TodoService.cs // 业务逻辑 ValidationFilter.cs // 验证过滤器

这与 aspnet-core 中"keep feature slices cohesive"(保持功能切片内聚,让页面、组件、端点、服务、数据访问和测试易于追踪)的默认假设一致。

二、请求与响应类型:用显式 DTO 约束 API 契约

技能文档强调"显式定义请求与响应 DTO/模型",这是 Minimal API 契约清晰度的核心。

1. 定义明确的 DTO 与模型类

不要直接把数据库实体暴露为 API 载荷,而应定义独立的请求/响应模型,让 API 契约与持久化模型解耦。companion 文档同样建议"keep request and response DTOs separate from persistence models"。

2. 用 record 类型表达不可变对象

对于请求/响应这类"创建后不再修改"的对象,C# 的record是天然选择:

public record CreateTodoRequest( string Title, bool IsComplete = false); public record TodoResponse( int Id, string Title, bool IsComplete, DateTimeOffset CreatedAt);

record 自带值相等性与with表达式支持,配合 init-only 属性可强化不可变性语义。

3. 用验证属性强制约束

在 DTO 属性上应用[Required]等验证特性,让无效请求在到达业务逻辑之前就被拦截:

public record CreateTodoRequest { [Required, MinLength(1), MaxLength(200)] public string Title { get; init; } = ""; public bool IsComplete { get; init; } }

在支持的框架版本上,Minimal API 提供了内置验证支持(.NET 10 中可用AddValidation()),companion 文档建议优先使用内置验证而非另起一套并行验证基础设施。

4. 用 ProblemDetails 与 StatusCodePages 获得标准错误响应

不要为错误响应自造 JSON 结构,应复用 ASP.NET Core 的标准机制:

  • ProblemDetailsService:将错误编码为 RFC 7807 规范的application/problem+json响应(含type、title、status、detail、instance字段);
  • StatusCodePages:为未显式处理的 HTTP 状态码提供一致的错误页面/响应。

companion 文档 apis-minimal-and-controllers.md 在共享实践一节同样强调:"UseProblemDetailsfor errors instead of ad hoc JSON shapes"。

builder.Services.AddProblemDetails(); builder.Services.AddStatusCodePages(); var app = builder.Build(); app.UseStatusCodePages();

三、类型处理:让编译器替你保证响应契约

这是本技能的核心技术主张:用强类型让响应形状在编译期被固定下来。

1. 强类型路由参数

路由参数应声明为明确类型(int、Guid、DateOnly等),由模型绑定负责转换,避免在处理器内手写解析与校验:

app.MapGet("/api/todos/{id:int}", (int id, TodoService svc) => svc.FindById(id) is { } todo ? Results.Ok(todo) : Results.NotFound());

{id:int}路由约束会拒绝非整数请求;Guid、DateOnly等类型参数则由绑定器自动完成类型转换。

2. 用Results<T1, T2>表达多种可能响应

Results<T1, T2>允许在编译期声明端点可能返回的响应类型集合,配合TypedResults工厂方法,使 OpenAPI 文档能自动推断出完整的响应形态:

app.MapGet("/api/todos/{id}", GetTodoById) .WithName("GetTodoById"); // 返回 200 或 404 Results<Ok<TodoResponse>, NotFound> GetTodoById(int id, TodoService svc) => svc.FindById(id) is { } todo ? TypedResults.Ok(new TodoResponse(todo.Id, todo.Title, todo.IsComplete, todo.CreatedAt)) : TypedResults.NotFound();

3. 优先返回TypedResults而非Results

TypedResults(如TypedResults.Ok、TypedResults.NotFound、TypedResults.Created)返回强类型的IResult实现,让 OpenAPI 元数据推断更精确;无类型化的Results.Ok会退化为运行时推断。companion 文档的"Good defaults"中也明确"preferTypedResultsover untyped results"。

4. 善用 C# 10+ 语言特性

  • 可空性注解(nullable annotations):对引用类型标注?,配合#nullable enable让可空性在编译期可见,减少空引用缺陷;
  • init-only 属性:对象初始化后不可再变,强化 DTO 不可变语义;
  • 顶层语句:Program.cs使用顶层语句让最小 API 项目保持"最小"。

资源创建场景还应遵循 companion 文档的建议,使用TypedResults.Created/CreatedAtRoute模式返回 201 与Location头。

四、OpenAPI 文档:从"能用"到"可发现、可消费"

技能文档的核心诉求是"correct types and comprehensive OpenAPI/Swagger documentation"——即让每个端点成为可发现、可消费的契约。

1. 使用 .NET 9 内置的 OpenAPI 文档支持

自 .NET 9 起,Microsoft.AspNetCore.OpenApi包提供了内置的 OpenAPI 文档生成能力,无需引入第三方 Swashbuckle 即可产出 OpenAPI 3.1 文档:

builder.Services.AddOpenApi(); var app = builder.Build(); app.MapOpenApi(); // 暴露 /openapi/{documentName}.json

这也解释了 autoskills 为何将Microsoft.AspNetCore.OpenApi作为检测 ASP.NET Minimal API 项目的信号——它是现代 .NET 项目内置 OpenAPI 能力的标准入口。

2. 定义操作的 summary 与 description

在端点处理器文档注释中编写摘要与详细说明,使生成的文档对消费方(前端、其他服务、AI 代理)更友好:

/// <summary>返回指定 ID 的待办事项。</summary> /// <param name="id">待办事项的唯一标识。</param> /// <returns>200 与待办事项详情,或 404。</returns> app.MapGet("/api/todos/{id}", GetTodoById) .WithName("GetTodoById") .WithSummary("Returns a single todo by id") .WithDescription("Fetches the todo with the given id. Returns 404 when it does not exist.");

3. 用WithName添加 operationId

WithName()为操作设置唯一标识(OpenAPI 的operationId),这对客户端代码生成(如生成强类型 SDK)至关重要:

app.MapGet("/api/todos/{id}", GetTodoById).WithName("GetTodoById");

4. 用[Description()]描述属性与参数

对 DTO 属性与参数添加[Description()],让 OpenAPI schema 携带字段语义说明:

using System.ComponentModel; public record CreateTodoRequest( [property: Description("Title of the todo item.")] string Title, [property: Description("Whether the todo is already completed.")] bool IsComplete = false);

5. 设置正确的请求/响应内容类型

通过显式的Produces类型或TypedResults派生类型,确保请求与响应的 Content-Type(如application/json)在文档中正确呈现。使用TypedResults时,Results<T1, T2>的泛型参数会驱动 OpenAPI 推断响应 schema 与状态码。

6. 用文档转换器(Document Transformers)添加 servers、tags、security schemes

.NET 9的 OpenAPI 支持通过IDocumentTransformer在文档生成后做全局定制,例如注入服务器地址、统一安全方案、标签分类:

builder.Services.AddOpenApi(options => { options.AddDocumentTransformer((document, context, cancellationToken) => { document.Servers = new List<OpenApiServer> { new() { Url = "https://api.example.com" } }; document.SecuritySchemes["Bearer"] = new OpenApiSecurityScheme { Type = SecuritySchemeType.Http, Scheme = "bearer", BearerFormat = "JWT" }; return Task.CompletedTask; }); });

典型用途包括:部署环境不同的servers列表、tags归类、OAuth2/Bearer 等security schemes声明,以及全局info元数据。

7. 用模式转换器(Schema Transformers)定制 OpenAPI schema

ISchemaTransformer允许对特定 schema 做细粒度定制,例如为属性追加默认值、示例或扩展字段:

builder.Services.AddOpenApi(options => { options.AddSchemaTransformer((schema, context, cancellationToken) => { if (context.JsonTypeInfo.Type == typeof(CreateTodoRequest)) { schema.Example = new OpenApiObject { ["title"] = new OpenApiString("Buy groceries"), ["isComplete"] = new OpenApiBoolean(false) }; } return Task.CompletedTask; }); });

组合使用文档转换器与模式转换器,可以在不改动业务代码的前提下,让生成的 OpenAPI 文档达到对外发布标准。

五、配套技能与延伸阅读

  • aspnet-core:更广泛的 ASP.NET Core 技能,覆盖应用模型选择、管线、DI、安全、测试等;其 apis-minimal-and-controllers.md 是 Minimal API 与控制器 API 选型的补充参考;
  • minimal-api-file-upload:与本文技能同属aspnet-minimal-api技术组合,当你的项目需要文件上传端点时可一并参考;
  • dotnet-best-practices 等 .NET 系列技能由dotnet技术检测项统一触发,与本文技能协同指导整个 .NET 项目的编码质量。

总结

编写高质量的 ASP.NET Minimal API 端点,本质上是在四个层面持续做对:结构上用MapGroup、端点过滤器和功能文件夹组织代码;契约上用显式 DTO、record 与验证属性约束请求/响应形状;类型上用强类型参数与TypedResults/Results<T1,T2>让响应在编译期固定;文档上用 .NET 9 内置 OpenAPI 支持配合WithName、描述与文档/模式转换器,把端点变成机器可读、可发现、可消费的契约。把这套规范落到你的 .NET 项目中,AI 助手、前端团队与外部消费者都能基于同一份准确契约高效协作。

【免费下载链接】autoskills

One command. Your entire AI skill stack. Installed.

项目地址:https://gitcode.com/gh_mirrors/au/autoskills
点击查看免费下载

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询