Start Debugging

Minimal APIs vs controllers en ASP.NET Core 11: ¿cuál deberías elegir en 2026?

Elige minimal APIs por defecto en ASP.NET Core 11. Usa controllers solo cuando necesites características de MVC que minimal APIs todavía no iguala: enrutamiento basado en convenciones para muchas acciones, filtros estilo MVC o vistas Razor.

Si estás empezando un nuevo servicio HTTP sobre ASP.NET Core 11 y tratas de decidir entre minimal APIs y controllers, elige minimal APIs. Las razones de 2022 para seguir usando controllers (sin filtros, sin validación, OpenAPI débil, sin grupos de rutas, sin Native AOT) han desaparecido. A partir de .NET 11, las minimal APIs tienen filtros de endpoint, grupos de rutas, un documento OpenAPI integrado vía Microsoft.AspNetCore.OpenApi, validación de parámetros con [Validate] en Microsoft.AspNetCore.Http.Validation, TypedResults y soporte completo para Native AOT. Los controllers siguen siendo la herramienta correcta para dos casos específicos: necesitas vistas Razor (MVC o Razor Pages) en el mismo proyecto, o estás manteniendo una base de código grande con cientos de acciones decoradas con [Route] que funcionan hoy.

Cada ejemplo de código en esta publicación apunta a <TargetFramework>net11.0</TargetFramework> con <LangVersion>14.0</LangVersion> y usa los paquetes de ASP.NET Core 11 que vienen con el SDK GA de .NET 11.

Matriz de características

CaracterísticaMinimal APIs (ASP.NET Core 11)Controllers (ASP.NET Core 11)
EnrutamientoEnrutamiento por endpoints, MapGet/MapPost, grupos de rutasEnrutamiento por endpoints, atributos o convenciones
Filtros por endpointIEndpointFilter, AddEndpointFilter<T>IActionFilter, IAsyncActionFilter
Inferencia de origen del bindingReglas de binding por parámetro, [FromBody] opcional[FromBody], [FromQuery], [FromForm], etc.
Validación[Validate] en Microsoft.AspNetCore.Http.Validation (ASP.NET Core 11)ModelState con DataAnnotations, ApiController
Helpers de resultadoTypedResults, ResultsIActionResult, ActionResult<T>, Problem()
OpenAPI / SwaggerMicrosoft.AspNetCore.OpenApi 11, sin plomería extraMicrosoft.AspNetCore.OpenApi 11 + convenciones
Native AOTTotalmente soportado (PublishAot=true funciona)Limitado; AddControllers aún emite advertencias de trim
Vistas Razor / Razor PagesNo soportado (sin pipeline de renderizado de vistas)Soportado (AddControllersWithViews, Razor Pages)
Antiforgery (POST de formulario con cookies)app.UseAntiforgery() + [FromForm]Integrado en MVC por defecto con [ValidateAntiForgeryToken]
Boilerplate por endpoint1 lambda + 1 línea MapX1 clase + 1 método + atributos
Descubribilidad en una base de 200 endpointsArchivos por grupo / métodos de extensiónUna clase de controller por recurso
Tamaño de binario publicado con AOTEl más pequeño del frameworkMayor; pipeline completo de MVC
Rendimiento (endpoint JSON pequeño, estilo TechEmpower)~3-5% mayor que controllersReferencia base

Los números de la última fila vienen de los propios benchmarks del equipo de .NET sobre ASP.NET Core 11. Trata la diferencia como “cercana al ruido” para la mayoría de las aplicaciones. La razón para preferir minimal APIs no es la diferencia de rendimiento. Es la superficie más pequeña y la historia de AOT.

Cuándo minimal APIs es la elección correcta

Usa minimal APIs por defecto para cualquier servicio HTTP nuevo en ASP.NET Core 11. Los casos específicos donde brillan:

Un endpoint pequeño pero 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 carga el conjunto mínimo de servicios, que es lo que hace viable Native AOT. MapGroup más RequireAuthorization es la historia de grupos de rutas / políticas compartidas que no existía antes de .NET 7. [Validate] es el atributo de ASP.NET Core 11 que activa Microsoft.AspNetCore.Http.Validation, el validador respaldado por un generador de código fuente que reemplaza el pipeline de DataAnnotations exclusivo de controllers. El tipo de retorno Results<TOk, TErr> es lo que hace que el documento OpenAPI sea preciso sin ninguna decoración Produces adicional.

Cuándo los controllers son la elección correcta

Recurre a controllers cuando una de estas afirmaciones sea cierta:

Un controller comparable para los endpoints de facturas:

// .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);
    }
}

Dos endpoints, el doble de boilerplate, sin ganancia funcional. Ese es el patrón que hace de minimal APIs la opción por defecto en 2026: en código nuevo, escribes menos para expresar lo mismo.

El benchmark

La diferencia de rendimiento entre los dos estilos en ASP.NET Core 11 es pequeña pero consistente en endpoints JSON pequeños. Los benchmarks oficiales del equipo de .NET (https://github.com/aspnet/Benchmarks, resultados de GA de ASP.NET Core 11 publicados en noviembre de 2025) reportan:

EscenarioMinimal APIs (RPS)Controllers (RPS)Delta
GET /json (objeto único, 200 bytes)1 160 0001 120 000+3,6%
POST /json (echo, cuerpo de 1 KB)590 000565 000+4,4%
GET /plaintext (TechEmpower)7 250 0007 250 0000%
Arranque en frío (AOT, dotnet publish -aot)70 ms280 ms-75%

Metodología, resumida de las ejecuciones publicadas: ASP.NET Core 11 GA, Linux x64, máquinas Citrine, wrk dirigiendo Kestrel con 256 conexiones concurrentes, ejecuciones de 30 segundos promediadas sobre cinco muestras. La fila de arranque en frío se publica desde el mismo laboratorio usando time sobre el binario AOT en la primera ejecución. plaintext es idéntico porque el costo del pipeline de la solicitud está dominado por el parseo de Kestrel, no por la forma del endpoint.

La lectura honesta de estos números: si estás limitado por CPU en un endpoint JSON caliente, podrías recuperar un 3-5% cambiando de controllers a minimal APIs. Las aplicaciones reales pasan el tiempo en EF Core o en salidas HTTP, no en el dispatcher de endpoints. La fila de arranque en frío, sin embargo, es real: si estás desplegando a AWS Lambda o Azure Functions, el delta de AOT es la diferencia entre un servicio que calienta en menos de un segundo y uno que no. El acompañamiento detallado sobre reducir el arranque en frío de Lambda en .NET 11 recorre el flujo de publicación AOT que produce esos números.

El obstáculo que decide por ti

Tres restricciones deciden esto sin opinar:

Lo que minimal APIs todavía no hace

Algunas cosas que controllers hace y que minimal APIs en ASP.NET Core 11 aún no iguala:

Si alguna de esas tres es clave en tu diseño, la relación costo-beneficio se vuelve a inclinar hacia controllers. Para el 95% de los nuevos servicios ASP.NET Core 11, no lo es.

La elección, restablecida

Por defecto, usa minimal APIs en ASP.NET Core 11. Son la forma con menos boilerplate, lista para AOT y limpia para OpenAPI de construir un servicio HTTP JSON en .NET 11. Las razones históricas para preferir controllers (filtros, validación, grupos de rutas, OpenAPI, convenciones) han sido cerradas una por una a lo largo de .NET 7, 8, 10 y 11. Lo que queda es un pequeño conjunto de características de MVC (vistas Razor, model binders, action constraints) que los controllers todavía poseen. Si tu proyecto necesita eso, usa controllers para las partes que lo requieran y minimal APIs para el resto. Los dos estilos conviven en una sola WebApplication sin problemas.

Relacionado

Fuentes

Comments

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

< Volver