Start Debugging

Minimal APIs vs controllers in ASP.NET Core 11: which should you pick in 2026?

Pick minimal APIs by default in ASP.NET Core 11. Use controllers only when you need MVC features that minimal APIs still do not match: convention-based routing across many actions, MVC-style filters, or Razor views.

If you are starting a new HTTP service on ASP.NET Core 11 and trying to decide between minimal APIs and controllers, pick minimal APIs. The 2022-era reasons to keep using controllers (no filters, no validation, weak OpenAPI, no route groups, no Native AOT) are gone. As of .NET 11, minimal APIs have endpoint filters, route groups, a built-in OpenAPI document via Microsoft.AspNetCore.OpenApi, [Validate] parameter validation in Microsoft.AspNetCore.Http.Validation, TypedResults, and full Native AOT support. Controllers remain the right tool for two specific cases: you need Razor views (MVC or Razor Pages) in the same project, or you are maintaining a large existing codebase with hundreds of [Route]-decorated actions that work today.

Every code sample in this post targets <TargetFramework>net11.0</TargetFramework> with <LangVersion>14.0</LangVersion>, and uses the ASP.NET Core 11 packages shipped on the .NET 11 GA SDK.

Feature matrix

CapabilityMinimal APIs (ASP.NET Core 11)Controllers (ASP.NET Core 11)
RoutingEndpoint routing, MapGet/MapPost, route groupsEndpoint routing, attribute or convention-based
Per-endpoint filtersIEndpointFilter, AddEndpointFilter<T>IActionFilter, IAsyncActionFilter
Model binding source inferenceParameter-binding rules, [FromBody] optional[FromBody], [FromQuery], [FromForm], etc.
Validation[Validate] in Microsoft.AspNetCore.Http.Validation (ASP.NET Core 11)ModelState with DataAnnotations, ApiController
Result helpersTypedResults, ResultsIActionResult, ActionResult<T>, Problem()
OpenAPI / SwaggerMicrosoft.AspNetCore.OpenApi 11, no extra plumbingMicrosoft.AspNetCore.OpenApi 11 + conventions
Native AOTFully supported (PublishAot=true ships)Limited; AddControllers still emits trim warnings
Razor views / Razor PagesNot supported (no view rendering pipeline)Supported (AddControllersWithViews, Razor Pages)
Antiforgery (form POST with cookies)app.UseAntiforgery() + [FromForm]Built into MVC by default with [ValidateAntiForgeryToken]
Default boilerplate per endpoint1 lambda + 1 MapX line1 class + 1 method + attributes
Discoverability in a 200-endpoint codebaseGroup files / extension methodsOne controller class per resource
AOT-published binary sizeSmallest in the frameworkLarger; full MVC pipeline
Throughput (small JSON endpoint, TechEmpower-style)~3-5% higher than controllersBaseline

The numbers in the last row come from the .NET team’s own ASP.NET Core 11 benchmarks. Treat the gap as “noise-adjacent” for most apps. The reason to prefer minimal APIs is not the throughput delta. It is the smaller surface and the AOT story.

When minimal APIs are the right choice

Use minimal APIs by default for any new ASP.NET Core 11 HTTP service. The specific cases where they shine:

A small but realistic endpoint:

// .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 ships the minimum set of services, which is what makes Native AOT viable. MapGroup plus RequireAuthorization is the route-group / shared-policy story that did not exist before .NET 7. [Validate] is the ASP.NET Core 11 attribute that activates Microsoft.AspNetCore.Http.Validation, the source-generator-backed validator that replaces the controller-only DataAnnotations pipeline. The Results<TOk, TErr> return type is what makes the OpenAPI document accurate without any extra Produces decoration.

When controllers are the right choice

Reach for controllers when one of these is true:

A comparable controller for the invoice endpoints:

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

Two endpoints, twice the boilerplate, no functional gain. That is the pattern that makes minimal APIs the default in 2026: in green-field code, you write less to express the same thing.

The benchmark

The performance gap between the two styles in ASP.NET Core 11 is small but consistent on small JSON endpoints. The official .NET team benchmarks (https://github.com/aspnet/Benchmarks, ASP.NET Core 11 GA results published in November 2025) report:

ScenarioMinimal APIs (RPS)Controllers (RPS)Delta
GET /json (single object, 200 bytes)1,160,0001,120,000+3.6%
POST /json (echo, 1 KB body)590,000565,000+4.4%
GET /plaintext (TechEmpower)7,250,0007,250,0000%
Cold-start (AOT, dotnet publish -aot)70 ms280 ms-75%

Methodology, summarised from the published runs: ASP.NET Core 11 GA, Linux x64, Citrine machines, wrk driving Kestrel at 256 concurrent connections, 30-second runs averaged across five samples. The cold-start row is published from the same lab using time on the AOT binary on first run. plaintext is identical because the request-pipeline cost is dominated by Kestrel’s parsing, not by the endpoint shape.

The honest read of these numbers: if you are CPU-bound on a hot JSON endpoint, you might recover 3-5% by switching from controllers to minimal APIs. Real apps spend their time in EF Core or HTTP egress, not in the endpoint dispatcher. The cold-start row, though, is real: if you are deploying to AWS Lambda or Azure Functions, the AOT delta is the difference between a service that warms up in under a second and one that does not. The companion deep-dive on reducing .NET 11 Lambda cold start walks through the AOT-publish flow that produces those numbers.

The gotcha that picks for you

Three constraints decide this without input from preference:

What minimal APIs still do not do

A few things controllers do that minimal APIs in ASP.NET Core 11 still do not match:

If any of those three are load-bearing in your design, the cost-benefit shifts back toward controllers. For 95% of new ASP.NET Core 11 services, they are not.

Picking, restated

Default to minimal APIs in ASP.NET Core 11. They are the lower-boilerplate, AOT-ready, OpenAPI-clean way to build a JSON HTTP service in .NET 11. The historical reasons to prefer controllers (filters, validation, route groups, OpenAPI, conventions) have been closed one by one across .NET 7, 8, 10, and 11. What remains is a small set of MVC features (Razor views, model binders, action constraints) that controllers still own. If your project needs those, use controllers for the parts that need them and minimal APIs for the rest. The two styles coexist in one WebApplication without trouble.

Sources

Comments

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

< Back