wen aidev
Published on

ASP.NET Core Web API(二):Minimal API 輕量高效設計

.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 真正的優勢是:

  1. AOT 編譯支援(Controller 目前不完整支援)
  2. 啟動時間更快(適合 Serverless 冷啟動)
  3. 程式碼更少(適合 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 vs Controller-based API 對照表

圖說:Minimal API 和 Controller 不是誰取代誰,而是不同場景的不同工具。

維度Controller-basedMinimal API
啟動速度較慢更快
AOT 支援有限完整
程式碼量較多(有大量 boilerplate)
組織結構框架幫你規定好需要自己設計
Model Validation自動([ApiController]手動或用 FluentValidation
Filter / Middleware豐富的內建 FilterIEndpointFilter(.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。

延伸閱讀

留言討論