跳到主要内容

4.3 Controller 与 Minimal API 对比

ASP.NET Core 同时支持 Controller 和 Minimal API。两者都能构建生产级 Web API,差异不在“谁更高级”,而在组织方式、约定数量、可扩展点和团队协作成本。

学习目标

  • 能解释 Controller 与 Minimal API 的路由、绑定和返回结果差异。
  • 能根据项目规模、团队习惯和接口复杂度选择实现方式。
  • 能把同一个 Todo 接口分别写成 Minimal API 和 Controller。
  • 能避免把两种风格混在一起导致结构混乱。

应用场景

  • 小型服务、内部工具或教学项目选择 Minimal API 快速起步。
  • 中大型业务系统使用 Controller 组织资源、过滤器和版本演进。
  • 在遗留 MVC 项目中继续维护 Controller,同时新增少量轻量接口。
  • 团队制定后端代码规范时统一接口组织方式。

核心概念

对比点Minimal APIController
路由定义Program.cs 或扩展方法中使用 MapGetMapPost通过 [Route][HttpGet] 等特性定义
代码组织路由处理器函数更直接,需要主动拆分类和方法天然按资源分组
返回结果常用 ResultsTypedResultsIResult常用 ActionResult<T>IActionResult
横切逻辑使用中间件、Endpoint Filter、扩展方法使用中间件、Action Filter、Authorization Filter
学习曲线入口少,上手快约定更多,但大型项目更规整

案例:同一个查询接口的两种写法

业务要求:根据任务 ID 查询 Todo,找不到返回 404,找到返回 DTO。

Minimal API 写法

app.MapGet("/todos/{id:int}", async (
int id,
ITodoQueryService todoService,
CancellationToken cancellationToken) =>
{
var todo = await todoService.GetByIdAsync(id, cancellationToken);
return todo is null ? Results.NotFound() : Results.Ok(todo);
})
.WithName("GetTodoById")
.WithOpenApi();

Minimal API 的优势是路径、参数、服务注入和返回结果都在一处,非常适合小接口和示例代码。缺点是接口变多后,必须主动把路由注册拆到扩展方法或模块类里。

Controller 写法

[ApiController]
[Route("api/todos")]
public sealed class TodosController(ITodoQueryService todoService) : ControllerBase
{
[HttpGet("{id:int}", Name = "GetTodoById")]
[ProducesResponseType<TodoDto>(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<ActionResult<TodoDto>> GetById(
int id,
CancellationToken cancellationToken)
{
var todo = await todoService.GetByIdAsync(id, cancellationToken);
return todo is null ? NotFound() : Ok(todo);
}
}

Controller 的优势是资源边界清晰,特性、过滤器和返回类型约定成熟。对于接口很多、权限规则复杂、团队成员较多的项目,它通常更容易统一风格。

示例:Controller 项目配置

如果项目使用 Controller,需要注册 MVC Controller 服务,并把 Controller 映射到请求管线。

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddOpenApi();

var app = builder.Build();

app.MapControllers();

app.Run();

Minimal API 和 Controller 可以共存,但应有明确边界。例如:核心业务 API 全部用 Controller,健康检查和内部探针用 Minimal API;不要同一个资源一半写在 Controller,一半散落在 Program.cs

选型建议

情况推荐
只有少量接口、原型或教学示例Minimal API
接口按资源分组,数量持续增加Controller
需要大量过滤器、模型约定和版本管理Controller
构建独立微服务或轻量后台接口Minimal API 或模块化 Minimal API
团队已有统一 Controller 规范继续使用 Controller

重点难点

  • Minimal API 不等于所有代码都写在 Program.cs,复杂项目仍要拆分注册方法和业务服务。
  • Controller 不等于传统 MVC 页面;Web API Controller 通常继承 ControllerBase,不返回 View。
  • 两种写法都应该返回 DTO,不要直接暴露 EF Core 实体。
  • 认证授权、中间件、依赖注入、配置和日志是共享机制,不依赖某一种路由风格。
  • 团队项目中最重要的是一致性,同一资源最好只采用一种接口组织方式。

常见误区

误区推荐做法
认为 Minimal API 只能写玩具项目小而清晰的服务可以使用模块化 Minimal API
认为 Controller 已经过时大型业务 API 仍然非常适合 Controller
两种风格随意混用给共存场景设定明确边界
路由处理器直接写数据库细节注入服务层,保持入口代码薄
只按个人喜好选型结合接口规模、团队规范和扩展点选择

练习

  • 把一个 MapPost("/todos") 改写成 TodosController.Create
  • 给 Controller 示例增加 POST /api/todos,返回 201 和资源地址。
  • 把 5 个 Minimal API 路由移动到 MapTodoEndpoints 扩展方法中。
  • 写一份团队规范:哪些接口用 Controller,哪些接口允许用 Minimal API。

延伸阅读