- Published on
ASP.NET Core Web API(二):Minimal API 輕量高效設計
Table of Contents
.NET 6 推出 Minimal API 的時候,很多人第一眼看到這段程式碼是有點震驚的:
var app = WebApplication.Create(args);
app.MapGet("/hello", () => "Hello, World!");
app.Run();
這就是一個完整的 HTTP API 服務器。沒有 Controller、沒有 Startup.cs、沒有一堆 Attribute,三行搞定。
相比上一篇的 Controller 架構,這看起來簡單太多了。但問題是:這個「簡單」能撐多大的應用?什麼時候它會變成噩夢?
Minimal API 的核心概念
Minimal API 的哲學是:把路由定義當成一等公民,直接在 Program.cs 掛 handler。
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddScoped<ITodoService, TodoService>();
var app = builder.Build();
// 直接 Map 路由和 Handler
app.MapGet("/api/todos", async (ITodoService service) =>
await service.GetAllAsync());
app.MapGet("/api/todos/{id}", async (int id, ITodoService service) =>
{
var todo = await service.GetByIdAsync(id);
return todo is null ? Results.NotFound() : Results.Ok(todo);
});
app.MapPost("/api/todos", async (CreateTodoRequest request, ITodoService service) =>
{
var todo = await service.CreateAsync(request);
return Results.CreatedAtRoute("GetTodo", new { id = todo.Id }, todo);
});
app.Run();
注意幾個亮點:
直接從 Lambda 參數拿 DI
ITodoService service 直接出現在 Lambda 參數裡,ASP.NET Core 會自動從 DI Container 取出來注入。不需要 Constructor Injection,不需要類別,直接用。
這是 Minimal API 的「魔法」之一,但底下的原理和 Controller 一模一樣,都是走 DI Container。
Results 取代 ActionResult
Controller 時代用 return Ok(data) / return NotFound(),Minimal API 改成 Results.Ok(data) / Results.NotFound()。語意相同,就是 namespace 不同。
效能差異真的有差嗎?
有,但可能沒你想的那麼大。根據微軟的 TechEmpower Benchmarks:
- Minimal API 比 Controller-based 快約 5~15%,主要省在 MVC Pipeline 的 overhead
- 這個差距對大多數業務 API 來說不是決策因素,真正的瓶頸通常是 DB query 或外部服務
Minimal API 真正的優勢是:
- AOT 編譯支援(Controller 目前不完整支援)
- 啟動時間更快(適合 Serverless 冷啟動)
- 程式碼更少(適合 Microservice 的單一職責 Service)
「5000 行 Program.cs」問題
Minimal API 最常被批評的就是:如果所有路由都塞在 Program.cs,它很快就會變成一坨無法維護的程式碼。
這不是假設,這是真實發生過的災難。
解法是用 Route Groups 加上 Extension Methods 把路由模組化:
// TodoEndpoints.cs
public static class TodoEndpoints
{
public static IEndpointRouteBuilder MapTodoEndpoints(
this IEndpointRouteBuilder routes)
{
var group = routes.MapGroup("/api/todos")
.WithTags("Todos")
.RequireAuthorization(); // 整組加驗證
group.MapGet("/", GetAll);
group.MapGet("/{id}", GetById).WithName("GetTodo");
group.MapPost("/", Create);
group.MapPut("/{id}", Update);
group.MapDelete("/{id}", Delete);
return routes;
}
static async Task<IResult> GetAll(ITodoService service)
=> Results.Ok(await service.GetAllAsync());
static async Task<IResult> GetById(int id, ITodoService service)
{
var todo = await service.GetByIdAsync(id);
return todo is null ? Results.NotFound() : Results.Ok(todo);
}
static async Task<IResult> Create(
CreateTodoRequest request, ITodoService service)
{
var todo = await service.CreateAsync(request);
return Results.CreatedAtRoute("GetTodo", new { id = todo.Id }, todo);
}
// ...
}
然後 Program.cs 就乾淨了:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddScoped<ITodoService, TodoService>();
var app = builder.Build();
app.MapTodoEndpoints();
app.MapProductEndpoints();
app.MapUserEndpoints();
app.Run();
這樣兼顧了 Minimal API 的輕量,又解決了擴展性問題。
Minimal API vs Controller 深度對照
圖說:Minimal API 和 Controller 不是誰取代誰,而是不同場景的不同工具。
| 維度 | Controller-based | Minimal API |
|---|---|---|
| 啟動速度 | 較慢 | 更快 |
| AOT 支援 | 有限 | 完整 |
| 程式碼量 | 較多(有大量 boilerplate) | 少 |
| 組織結構 | 框架幫你規定好 | 需要自己設計 |
| Model Validation | 自動([ApiController]) | 手動或用 FluentValidation |
| Filter / Middleware | 豐富的內建 Filter | IEndpointFilter(.NET 7+) |
| 大型專案適合度 | 高(有慣例可循) | 需要紀律(Route Groups) |
| 微服務 / Serverless | 可以但略重 | 非常適合 |
什麼時候選 Minimal API?
選 Minimal API 的訊號
Microservice / 單一職責 Service:每個 Service 只做一件事,端點少,Minimal API 完美匹配
Serverless(Azure Functions、AWS Lambda) :冷啟動時間很關鍵,Minimal API + AOT 是最強選擇
快速原型:3 天 Hackathon,不想浪費時間設定 Controller
下一篇的 Vertical Slice Architecture:兩者天然契合,每個 Feature Slice 用 Endpoint Group 對應
如果你的專案規模持續成長、端點數超過 30 個、或需要複雜的跨切關注點(Auth、Logging、Rate Limiting),再認真考慮要不要切回 Controller-based 或引入 Vertical Slice。
混用是被允許的
Minimal API 和 Controller 可以在同一個 ASP.NET Core 應用裡共存:
app.MapControllers(); // 繼續維護舊的 Controller
app.MapTodoEndpoints(); // 新功能用 Minimal API
這讓你可以漸進式遷移,不用一口氣重寫整個 API。