# ASP.NET Core Minimal APIs nel 2026: Architettura, Prestazioni e Domande per Colloqui > Le Minimal APIs in ASP.NET Core offrono un approccio snello per creare endpoint HTTP con meno codice boilerplate, supporto Native AOT e prestazioni ottimizzate per microservizi e deployment serverless. - Published: 2026-07-24 - Updated: 2026-07-24 - Author: SharpSkill - Reading time: 5 min --- Le Minimal APIs di ASP.NET Core eliminano la complessità dei controller MVC tradizionali, offrendo un approccio semplificato per costruire endpoint HTTP con significativamente meno codice boilerplate. Introdotte in .NET 6 e perfezionate attraverso .NET 8, 9 e ora .NET 10, le Minimal APIs sono maturate in una scelta pronta per la produzione per microservizi, funzioni serverless e servizi web leggeri. > **Minimal APIs vs Controller** > > Le Minimal APIs utilizzano handler di route top-level definiti direttamente in Program.cs, mentre i controller MVC richiedono definizioni di classe, attributi e routing basato su convenzioni. Per semplici operazioni CRUD o microservizi con meno di 20 endpoint, le Minimal APIs riducono tipicamente il codice del 40-60%. ## Architettura delle Minimal APIs e Pipeline delle Richieste La pipeline delle richieste di ASP.NET Core elabora le richieste HTTP attraverso componenti middleware prima di raggiungere gli handler degli endpoint. Le Minimal APIs si integrano perfettamente con questa pipeline offrendo una sintassi più dichiarativa per la definizione delle route. ```csharp // Program.cs var builder = WebApplication.CreateBuilder(args); // Registrazione dei servizi per dependency injection builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); builder.Services.AddScoped(); var app = builder.Build(); // Configurazione della pipeline middleware app.UseExceptionHandler("/error"); app.UseHttpsRedirection(); app.UseAuthorization(); // Definizioni degli endpoint Minimal API app.MapGet("/products", async (IProductRepository repo) => Results.Ok(await repo.GetAllAsync())); app.MapGet("/products/{id:int}", async (int id, IProductRepository repo) => await repo.GetByIdAsync(id) is Product product ? Results.Ok(product) : Results.NotFound()); app.Run(); ``` Questo pattern centralizza le definizioni delle route mantenendo pieno accesso al container di dependency injection. Il parametro `IProductRepository` dimostra l'iniezione senza costruttore direttamente nei delegate handler. ## Route Groups e Organizzazione degli Endpoint Con la crescita delle applicazioni, l'organizzazione degli endpoint diventa critica. I Route Groups, introdotti in .NET 7, forniscono namespacing e configurazione condivisa senza sacrificare l'approccio minimale. ```csharp // ProductEndpoints.cs public static class ProductEndpoints { public static void MapProductEndpoints(this WebApplication app) { var group = app.MapGroup("/api/products") .WithTags("Products") .RequireAuthorization(); group.MapGet("/", GetAllProducts); group.MapGet("/{id:int}", GetProductById); group.MapPost("/", CreateProduct) .Accepts("application/json") .Produces(StatusCodes.Status201Created); group.MapPut("/{id:int}", UpdateProduct); group.MapDelete("/{id:int}", DeleteProduct) .RequireAuthorization("AdminOnly"); } private static async Task GetAllProducts( IProductRepository repo, CancellationToken ct) { var products = await repo.GetAllAsync(ct); return Results.Ok(products); } private static async Task GetProductById( int id, IProductRepository repo, CancellationToken ct) { var product = await repo.GetByIdAsync(id, ct); return product is not null ? Results.Ok(product) : Results.NotFound(); } private static async Task CreateProduct( CreateProductRequest request, IProductRepository repo, IValidator validator, CancellationToken ct) { var validation = await validator.ValidateAsync(request, ct); if (!validation.IsValid) return Results.ValidationProblem(validation.ToDictionary()); var product = await repo.CreateAsync(request.ToProduct(), ct); return Results.Created($"/api/products/{product.Id}", product); } } ``` Chiamando `app.MapProductEndpoints()` in Program.cs vengono registrate tutte le route dei prodotti con requisiti di autorizzazione condivisi e metadati OpenAPI. Questa struttura scala bene per applicazioni con centinaia di endpoint. ## Binding dei Parametri e Validazione Le Minimal APIs supportano multiple sorgenti di binding: parametri di route, query string, header, corpi delle richieste e servizi dal DI container. Comprendere la precedenza del binding previene errori comuni nei colloqui tecnici. ```csharp // Program.cs - Esempi di binding dei parametri app.MapGet("/search", ( [FromQuery] string? query, // Query string esplicita [FromQuery] int page = 1, // Valore predefinito [FromQuery] int pageSize = 20, // Valore predefinito [FromHeader(Name = "X-Correlation-Id")] string? correlationId, ILogger logger) => { logger.LogInformation("Richiesta di ricerca: {Query}, Pagina: {Page}, CorrelationId: {CorrelationId}", query, page, correlationId); return Results.Ok(new { query, page, pageSize, correlationId }); }); // Binding di modelli complessi con validazione app.MapPost("/orders", async ( [FromBody] CreateOrderRequest request, [FromServices] IValidator validator, [FromServices] IOrderService orderService, HttpContext context, CancellationToken ct) => { var validationResult = await validator.ValidateAsync(request, ct); if (!validationResult.IsValid) { return Results.ValidationProblem( validationResult.Errors .GroupBy(e => e.PropertyName) .ToDictionary( g => g.Key, g => g.Select(e => e.ErrorMessage).ToArray())); } var userId = context.User.FindFirstValue(ClaimTypes.NameIdentifier); var order = await orderService.CreateOrderAsync(request, userId!, ct); return Results.Created($"/orders/{order.Id}", order); }); ``` L'attributo `[FromBody]` è opzionale per i tipi complessi ma migliora la leggibilità. FluentValidation si integra naturalmente attraverso la dependency injection, mantenendo la logica di validazione separata dagli handler degli endpoint. > **Priorità delle Sorgenti di Binding** > > Quando non viene specificato alcun attributo, le Minimal APIs deducono le sorgenti di binding: prima i parametri di route, poi le query string per i tipi semplici e il corpo della richiesta per i tipi complessi. Attributi espliciti come [FromQuery] o [FromBody] sovrascrivono questo comportamento. ## Ottimizzazione delle Prestazioni con Native AOT .NET 8 ha introdotto il supporto per la compilazione Native AOT (Ahead-of-Time) per le Minimal APIs, producendo eseguibili autonomi con tempi di avvio inferiori al millisecondo. Questa capacità rende le Minimal APIs ideali per deployment serverless dove la latenza di cold start è importante. ```csharp // Program.cs - Configurazione compatibile con AOT var builder = WebApplication.CreateSlimBuilder(args); // Serializzazione JSON compatibile con AOT builder.Services.ConfigureHttpJsonOptions(options => { options.SerializerOptions.TypeInfoResolverChain.Insert(0, AppJsonContext.Default); }); var app = builder.Build(); app.MapGet("/health", () => Results.Ok(new HealthResponse("Healthy", DateTime.UtcNow))); app.Run(); // Contesto del serializzatore JSON generato dal codice sorgente [JsonSerializable(typeof(HealthResponse))] [JsonSerializable(typeof(Product))] [JsonSerializable(typeof(List))] internal partial class AppJsonContext : JsonSerializerContext { } public record HealthResponse(string Status, DateTime CheckedAt); ``` Il metodo `CreateSlimBuilder` esclude funzionalità del framework non necessarie, mentre il `JsonSerializerContext` generato dal codice sorgente elimina la reflection a runtime per la serializzazione JSON. I binari AOT pubblicati per API semplici misurano tipicamente 10-15 MB rispetto agli 80+ MB per deployment self-contained standard. ## Typed Results e Metadati delle Risposte .NET 7 ha introdotto `TypedResults` per la verifica a compile-time dei tipi di risposta, migliorando l'accuratezza della documentazione OpenAPI e rilevando errori di tipo durante lo sviluppo. ```csharp // Risultati fortemente tipizzati con metadati OpenAPI app.MapGet("/products/{id:int}", async Task, NotFound, ProblemHttpResult>> ( int id, IProductRepository repo, CancellationToken ct) => { try { var product = await repo.GetByIdAsync(id, ct); return product is not null ? TypedResults.Ok(product) : TypedResults.NotFound(); } catch (Exception ex) { return TypedResults.Problem( detail: "Si è verificato un errore durante il recupero del prodotto", statusCode: StatusCodes.Status500InternalServerError); } }) .WithName("GetProductById") .WithOpenApi(operation => { operation.Summary = "Recupera un prodotto per ID"; operation.Description = "Restituisce i dettagli del prodotto o 404 se non trovato"; return operation; }); ``` Il tipo union `Results` dichiara tutti i possibili tipi di risposta, che i generatori Swagger/OpenAPI utilizzano per produrre documentazione accurata. Questo pattern è particolarmente prezioso nella preparazione per [domande di colloquio su ASP.NET Core](/blog/dotnet/aspnet-core-interview-questions-middleware-di-minimal-apis) sul design delle API. ## Endpoint Filter per Concern Trasversali Gli endpoint filter forniscono funzionalità simili ai middleware con scope su endpoint o gruppi specifici, gestendo concern come logging, caching e trasformazione delle richieste. ```csharp // ValidationFilter.cs public class ValidationFilter : IEndpointFilter where T : class { public async ValueTask InvokeAsync( EndpointFilterInvocationContext context, EndpointFilterDelegate next) { var validator = context.HttpContext .RequestServices .GetService>(); if (validator is null) return await next(context); var argument = context.Arguments .OfType() .FirstOrDefault(); if (argument is null) return await next(context); var validationResult = await validator.ValidateAsync(argument); if (!validationResult.IsValid) { return Results.ValidationProblem( validationResult.Errors .GroupBy(e => e.PropertyName) .ToDictionary( g => g.Key, g => g.Select(e => e.ErrorMessage).ToArray())); } return await next(context); } } // Utilizzo in Program.cs app.MapPost("/products", CreateProduct) .AddEndpointFilter>(); // Registrazione globale del filter tramite route group var api = app.MapGroup("/api") .AddEndpointFilter(async (context, next) => { var logger = context.HttpContext .RequestServices .GetRequiredService>(); var stopwatch = Stopwatch.StartNew(); var result = await next(context); stopwatch.Stop(); logger.LogInformation( "Endpoint {Method} {Path} completato in {ElapsedMs}ms", context.HttpContext.Request.Method, context.HttpContext.Request.Path, stopwatch.ElapsedMilliseconds); return result; }); ``` I filter vengono eseguiti nell'ordine di registrazione, con il filter più interno più vicino all'handler dell'endpoint. Questa architettura permette una separazione pulita della logica di validazione, logging e autorizzazione. ## Pattern di Autenticazione e Autorizzazione Le Minimal APIs supportano gli stessi meccanismi di autenticazione e autorizzazione dei controller MVC, con una sintassi di configurazione più dichiarativa. ```csharp // Program.cs - Setup autenticazione JWT builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidateAudience = true, ValidateLifetime = true, ValidateIssuerSigningKey = true, ValidIssuer = builder.Configuration["Jwt:Issuer"], ValidAudience = builder.Configuration["Jwt:Audience"], IssuerSigningKey = new SymmetricSecurityKey( Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"]!)) }; }); builder.Services.AddAuthorizationBuilder() .AddPolicy("AdminOnly", policy => policy.RequireRole("Admin")) .AddPolicy("PremiumUser", policy => policy.RequireClaim("subscription", "premium", "enterprise")); var app = builder.Build(); app.UseAuthentication(); app.UseAuthorization(); // Endpoint protetti app.MapGet("/admin/users", async (IUserService userService) => Results.Ok(await userService.GetAllUsersAsync())) .RequireAuthorization("AdminOnly"); app.MapGet("/profile", async (ClaimsPrincipal user, IUserService userService) => { var userId = user.FindFirstValue(ClaimTypes.NameIdentifier); var profile = await userService.GetProfileAsync(userId!); return Results.Ok(profile); }) .RequireAuthorization(); // Endpoint anonimo all'interno di un gruppo protetto var protectedGroup = app.MapGroup("/api/secure") .RequireAuthorization(); protectedGroup.MapGet("/public-info", () => Results.Ok("Questo è pubblico")) .AllowAnonymous(); ``` Il metodo di estensione `RequireAuthorization` accetta nomi di policy o può essere chiamato senza argomenti per richiedere qualsiasi utente autenticato. Comprendere questi pattern è essenziale per le [domande di colloquio su autenticazione e autorizzazione](/technologies/dotnet/interview-questions/authentication-authorization). ## Domande per Colloqui: Pattern Comuni I colloqui tecnici esplorano frequentemente le differenze tra Minimal APIs e controller tradizionali. La tabella seguente riassume le distinzioni chiave: | Aspetto | Minimal APIs | Controller MVC | |---------|--------------|----------------| | Boilerplate | Basso - delegate handler diretti | Più alto - classe + metodo + attributi | | Routing | Inline con `MapGet`, `MapPost` | Basato su attributi o convenzioni | | Model Binding | Automatico con attributi opzionali | Convenzione + attributi | | Filter | Endpoint filter | Action filter + middleware | | Supporto AOT | Pieno supporto da .NET 8 | Limitato, pesante uso di reflection | | Testabilità | Basato su funzioni, facile da testare | Richiede istanziazione del controller | | Ideale per | Microservizi, API semplici | Applicazioni grandi, workflow complessi | > **Suggerimento per il Colloquio** > > Quando viene chiesto "Quando scegliereste i controller invece delle Minimal APIs?", menzionate applicazioni complesse che richiedono action filter, personalizzazione del model binding, o codebase esistenti con pattern MVC consolidati. Le Minimal APIs eccellono per microservizi greenfield e funzioni serverless. ## Test degli Endpoint delle Minimal APIs I test di integrazione con `WebApplicationFactory` forniscono una verifica realistica degli endpoint senza deployare l'applicazione. ```csharp // ProductEndpointsTests.cs public class ProductEndpointsTests : IClassFixture> { private readonly HttpClient _client; private readonly WebApplicationFactory _factory; public ProductEndpointsTests(WebApplicationFactory factory) { _factory = factory.WithWebHostBuilder(builder => { builder.ConfigureServices(services => { // Sostituire il repository reale con un mock services.RemoveAll(); services.AddScoped(); }); }); _client = _factory.CreateClient(); } [Fact] public async Task GetProducts_ReturnsOkWithProductList() { // Act var response = await _client.GetAsync("/api/products"); // Assert response.StatusCode.Should().Be(HttpStatusCode.OK); var products = await response.Content .ReadFromJsonAsync>(); products.Should().NotBeNull(); products.Should().HaveCountGreaterThan(0); } [Fact] public async Task GetProductById_WithInvalidId_ReturnsNotFound() { // Act var response = await _client.GetAsync("/api/products/99999"); // Assert response.StatusCode.Should().Be(HttpStatusCode.NotFound); } [Fact] public async Task CreateProduct_WithValidRequest_ReturnsCreated() { // Arrange var request = new CreateProductRequest("Prodotto Test", 29.99m, "Descrizione Test"); // Act var response = await _client.PostAsJsonAsync("/api/products", request); // Assert response.StatusCode.Should().Be(HttpStatusCode.Created); response.Headers.Location.Should().NotBeNull(); } } ``` Per un'esplorazione più approfondita dei [pattern di Clean Architecture](/blog/dotnet/clean-architecture-dotnet-practical-guide) nelle applicazioni .NET, il layer dei servizi dovrebbe essere testato indipendentemente con unit test, mentre i test di integrazione verificano l'intera pipeline delle richieste. ## Conclusione Le Minimal APIs rappresentano l'approccio moderno per costruire servizi HTTP in ASP.NET Core: - I Route Groups e l'organizzazione degli endpoint scalano da semplici microservizi ad applicazioni complesse - Gli Endpoint filter forniscono una separazione pulita dei concern trasversali come validazione e logging - TypedResults permette la verifica a compile-time dei tipi di risposta e una documentazione OpenAPI accurata - La compilazione Native AOT fornisce tempi di avvio inferiori al millisecondo per deployment serverless - I test di integrazione con WebApplicationFactory garantiscono una verifica realistica degli endpoint Per la preparazione ai colloqui, concentrarsi sull'articolare quando le Minimal APIs sono appropriate rispetto ai controller tradizionali e dimostrare comprensione della pipeline delle richieste, della dependency injection e dei pattern di autenticazione. --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/it/blog/dotnet/aspnet-core-minimal-apis-architecture-performance-interview