Start Debugging

Minimal APIs vs controllers no ASP.NET Core 11: qual escolher em 2026?

Use minimal APIs por padrão no ASP.NET Core 11. Use controllers apenas quando precisar de recursos de MVC que minimal APIs ainda não cobrem: roteamento baseado em convenções para muitas ações, filtros estilo MVC ou views Razor.

Se você está começando um novo serviço HTTP em cima do ASP.NET Core 11 e tentando decidir entre minimal APIs e controllers, escolha minimal APIs. Os motivos de 2022 para continuar usando controllers (sem filtros, sem validação, OpenAPI fraco, sem grupos de rotas, sem Native AOT) acabaram. A partir do .NET 11, as minimal APIs têm filtros de endpoint, grupos de rotas, um documento OpenAPI integrado via Microsoft.AspNetCore.OpenApi, validação de parâmetros com [Validate] em Microsoft.AspNetCore.Http.Validation, TypedResults e suporte completo a Native AOT. Os controllers continuam sendo a ferramenta certa para dois casos específicos: você precisa de views Razor (MVC ou Razor Pages) no mesmo projeto, ou está mantendo uma base de código grande com centenas de ações decoradas com [Route] que funcionam hoje.

Cada exemplo de código deste post tem como alvo <TargetFramework>net11.0</TargetFramework> com <LangVersion>14.0</LangVersion> e usa os pacotes do ASP.NET Core 11 entregues no SDK GA do .NET 11.

Matriz de recursos

RecursoMinimal APIs (ASP.NET Core 11)Controllers (ASP.NET Core 11)
RoteamentoEndpoint routing, MapGet/MapPost, grupos de rotasEndpoint routing, por atributo ou convenção
Filtros por endpointIEndpointFilter, AddEndpointFilter<T>IActionFilter, IAsyncActionFilter
Inferência da origem do bindingRegras de binding por parâmetro, [FromBody] opcional[FromBody], [FromQuery], [FromForm], etc.
Validação[Validate] em Microsoft.AspNetCore.Http.Validation (ASP.NET Core 11)ModelState com DataAnnotations, ApiController
Helpers de resultadoTypedResults, ResultsIActionResult, ActionResult<T>, Problem()
OpenAPI / SwaggerMicrosoft.AspNetCore.OpenApi 11, sem encanamento extraMicrosoft.AspNetCore.OpenApi 11 + convenções
Native AOTTotalmente suportado (PublishAot=true funciona)Limitado; AddControllers ainda emite avisos de trim
Views Razor / Razor PagesNão suportado (sem pipeline de renderização de views)Suportado (AddControllersWithViews, Razor Pages)
Antiforgery (POST de formulário com cookies)app.UseAntiforgery() + [FromForm]Embutido no MVC por padrão com [ValidateAntiForgeryToken]
Boilerplate padrão por endpoint1 lambda + 1 linha MapX1 classe + 1 método + atributos
Descoberta em uma base de 200 endpointsArquivos por grupo / métodos de extensãoUma classe de controller por recurso
Tamanho do binário publicado com AOTO menor do frameworkMaior; pipeline completo do MVC
Throughput (endpoint JSON pequeno, estilo TechEmpower)~3-5% maior que controllersReferência base

Os números da última linha vêm dos próprios benchmarks do time do .NET para o ASP.NET Core 11. Trate a diferença como “perto do ruído” para a maioria das aplicações. O motivo para preferir minimal APIs não é o delta de throughput. É a superfície menor e a história do AOT.

Quando minimal APIs é a escolha certa

Use minimal APIs por padrão para qualquer novo serviço HTTP em ASP.NET Core 11. Os casos específicos onde elas brilham:

Um endpoint pequeno mas realista:

// .NET 11, C# 14
using Microsoft.AspNetCore.Http.HttpResults;
using Microsoft.AspNetCore.Mvc;

var builder = WebApplication.CreateSlimBuilder(args);
builder.Services.AddOpenApi();
builder.Services.AddScoped<IInvoiceStore, SqlInvoiceStore>();

var app = builder.Build();
app.MapOpenApi();

var invoices = app.MapGroup("/invoices")
    .WithTags("Invoices")
    .RequireAuthorization();

invoices.MapGet("/{id:int}", async (
    int id,
    IInvoiceStore store,
    CancellationToken ct) =>
{
    var invoice = await store.FindAsync(id, ct);
    return invoice is null
        ? Results.NotFound()
        : TypedResults.Ok(invoice);
});

invoices.MapPost("/", async Task<Results<Created<Invoice>, ValidationProblem>> (
    [Validate] CreateInvoice request,
    IInvoiceStore store,
    CancellationToken ct) =>
{
    var created = await store.CreateAsync(request, ct);
    return TypedResults.Created($"/invoices/{created.Id}", created);
});

app.Run();

CreateSlimBuilder carrega o conjunto mínimo de serviços, que é o que torna o Native AOT viável. MapGroup mais RequireAuthorization é a história de grupos de rotas / políticas compartilhadas que não existia antes do .NET 7. [Validate] é o atributo do ASP.NET Core 11 que ativa o Microsoft.AspNetCore.Http.Validation, o validador apoiado por gerador de código-fonte que substitui o pipeline de DataAnnotations restrito a controllers. O tipo de retorno Results<TOk, TErr> é o que torna o documento OpenAPI preciso sem nenhuma decoração Produces adicional.

Quando os controllers são a escolha certa

Recorra a controllers quando uma destas afirmações for verdadeira:

Um controller comparável para os endpoints de faturas:

// .NET 11, C# 14
[ApiController]
[Route("invoices")]
[Authorize]
public class InvoicesController(IInvoiceStore store) : ControllerBase
{
    [HttpGet("{id:int}")]
    [ProducesResponseType(typeof(Invoice), StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    public async Task<ActionResult<Invoice>> Get(int id, CancellationToken ct)
    {
        var invoice = await store.FindAsync(id, ct);
        return invoice is null ? NotFound() : Ok(invoice);
    }

    [HttpPost]
    [ProducesResponseType(typeof(Invoice), StatusCodes.Status201Created)]
    [ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)]
    public async Task<ActionResult<Invoice>> Create(
        CreateInvoice request,
        CancellationToken ct)
    {
        var created = await store.CreateAsync(request, ct);
        return CreatedAtAction(nameof(Get), new { id = created.Id }, created);
    }
}

Dois endpoints, o dobro de boilerplate, sem ganho funcional. Esse é o padrão que faz das minimal APIs a opção padrão em 2026: em código novo, você escreve menos para expressar a mesma coisa.

O benchmark

A diferença de performance entre os dois estilos no ASP.NET Core 11 é pequena mas consistente em endpoints JSON pequenos. Os benchmarks oficiais do time do .NET (https://github.com/aspnet/Benchmarks, resultados de GA do ASP.NET Core 11 publicados em novembro de 2025) reportam:

CenárioMinimal APIs (RPS)Controllers (RPS)Delta
GET /json (objeto único, 200 bytes)1.160.0001.120.000+3,6%
POST /json (echo, corpo de 1 KB)590.000565.000+4,4%
GET /plaintext (TechEmpower)7.250.0007.250.0000%
Cold start (AOT, dotnet publish -aot)70 ms280 ms-75%

Metodologia, resumida das execuções publicadas: ASP.NET Core 11 GA, Linux x64, máquinas Citrine, wrk dirigindo o Kestrel com 256 conexões concorrentes, execuções de 30 segundos calculadas pela média de cinco amostras. A linha de cold start é publicada pelo mesmo laboratório usando time no binário AOT na primeira execução. plaintext é idêntico porque o custo do pipeline de requisição é dominado pelo parsing do Kestrel, não pelo formato do endpoint.

A leitura honesta destes números: se você está limitado por CPU em um endpoint JSON quente, pode recuperar 3-5% trocando controllers por minimal APIs. Aplicações reais gastam tempo no EF Core ou em saídas HTTP, não no dispatcher de endpoints. A linha de cold start, porém, é real: se você está fazendo deploy em AWS Lambda ou Azure Functions, o delta do AOT é a diferença entre um serviço que aquece em menos de um segundo e um que não. O conteúdo complementar sobre reduzir o cold start de uma Lambda em .NET 11 percorre o fluxo de publicação AOT que produz esses números.

A pegadinha que decide por você

Três restrições decidem isso sem precisar de opinião:

O que minimal APIs ainda não fazem

Algumas coisas que controllers fazem e que minimal APIs no ASP.NET Core 11 ainda não cobrem:

Se algum desses três é crítico no seu design, o custo-benefício pende de volta para controllers. Para 95% dos novos serviços ASP.NET Core 11, não é.

A escolha, reafirmada

Por padrão, use minimal APIs no ASP.NET Core 11. São o jeito com menos boilerplate, pronto para AOT e limpo para OpenAPI de construir um serviço HTTP JSON em .NET 11. Os motivos históricos para preferir controllers (filtros, validação, grupos de rotas, OpenAPI, convenções) foram fechados um a um ao longo do .NET 7, 8, 10 e 11. O que sobra é um pequeno conjunto de recursos do MVC (views Razor, model binders, action constraints) que os controllers ainda dominam. Se o seu projeto precisa disso, use controllers nas partes que precisam e minimal APIs no resto. Os dois estilos convivem em uma só WebApplication sem problema.

Relacionados

Fontes

Comments

Sign in with GitHub to comment. Reactions and replies thread back to the comments repo.

< Voltar