Minimal API 绕过 MVC 管道,直接注册端点;绑定依赖类型与上下文而非特性,服务需用[FromServices]标记,Swagger需.WithName()显式命名,返回必须用Results.*()包装。

Minimal API 不是“简化版 MVC”,它绕过整个 MVC 管道,直接注册端点;用对了省一半代码,用错了连 400 都不知道为啥——核心问题不在写法,而在绑定逻辑、服务时机和返回规范。
MapGet/MapPost 参数绑定为什么总 400?
Minimal API 不认 [FromQuery]、[FromBody] 这类特性,绑定完全靠类型 + 名称 + 请求上下文自动推断,不是靠标注。
- 简单类型(
string、int、Guid)默认从路由段或查询字符串取值:比如app.MapGet("/user/{id}", (int id) => ...)中的id必须叫id(大小写敏感),且路径里得有{id} - 复杂类型(如
User)默认只接受Content-Type: application/json的请求体;发application/x-www-form-urlencoded或纯文本会直接 400,不报错细节 - 想混用路径参数和查询参数(比如
/users/{id}?page=2),不能传一个UserQuery类,得拆成两个参数:(int id, int page) - 要强制指定来源?必须用
AsParameters或实现BindAsync,加特性无效
依赖注入服务怎么在 handler 里用?
服务必须提前注册到 builder.Services,但不能在 MapXxx 外部提前解析;handler 内部要用 [FromServices] 显式标记,否则 DI 容器不会注入。
- 注册服务:在
var builder = WebApplication.CreateBuilder(args);之后、builder.Build()之前调用builder.Services.AddScoped<IUserService, UserService>(); - 使用服务:在 lambda 参数里写
([FromServices] IUserService svc) => svc.GetUsers();漏掉[FromServices]就会报No service for type 'IUserService' has been registered - 别在扩展方法里调用
builder.Services.Addxxx()或提前app.Services.GetService(),那会破坏生命周期顺序
为什么 Swagger 不显示接口名或分组?
Minimal API 默认不带任何元数据,Swagger 拿不到名称、分组、描述,只能生成一堆 Get、Post 这样的随机名。
- 每个端点必须链式调用
.WithName("Users_GetById"),否则 Swagger 里看不到可读名称 - 想归类到 “Users” 分组,得加
.WithMetadata(new EndpointNameMetadata("Users"))(部分文档工具识别) -
AddEndpointsApiExplorer()和AddSwaggerGen()必须在builder.Services中注册,UseSwagger()和UseSwaggerUI()必须在app.Run()前启用 - 所有
MapXxx调用也必须在app.Run()前完成,否则路由根本没注册进去
返回值为什么不能直接 return 对象?
Minimal API 要求显式使用 Results.*() 工厂方法包装返回值,直接 return new { Ok = true }; 会编译失败或运行时报错。
- 正确写法:
app.MapGet("/health", () => Results.Ok(new { Status = "Healthy" })) - 状态码控制靠工厂方法:
Results.Created(...)、Results.BadRequest(...)、Results.NotFound() - 想统一处理错误?不能靠过滤器,得手动在 handler 里判断并返回对应
Results.*() - 异步操作必须用
async/await+Task<IResult>返回类型,不能只写async () => await svc.GetAsync()
最容易被忽略的是三件事:路由注册必须在 app.Run() 前、复杂对象绑定强依赖 Content-Type、所有返回必须走 Results.*()。这三点踩中任意一个,项目就能跑起来,但接口永远不对劲。


















