25 Domande ASP.NET Core per Colloqui: Middleware, DI e Minimal APIs

Guida completa alle domande sui colloqui ASP.NET Core: middleware, dependency injection e Minimal APIs con esempi di codice e best practice.

Domande ASP.NET Core per colloqui su middleware, DI e Minimal APIs

Le domande sui colloqui tecnici per posizioni ASP.NET Core rappresentano una sfida significativa per gli sviluppatori che desiderano dimostrare competenze avanzate nel framework. La comprensione approfondita di middleware, dependency injection e Minimal APIs costituisce il fondamento per superare con successo questi colloqui e distinguersi dalla concorrenza.

ASP.NET Core ha rivoluzionato lo sviluppo web nel mondo .NET, introducendo un'architettura modulare e performante che richiede agli sviluppatori una conoscenza dettagliata dei suoi meccanismi interni. Questo articolo presenta le 25 domande più frequenti nei colloqui tecnici, organizzate per argomento e accompagnate da esempi di codice pronti per essere discussi.

Consiglio per il colloquio

Durante un colloquio tecnico su ASP.NET Core, i recruiter valutano non solo la conoscenza teorica ma anche la capacità di applicare i concetti in scenari reali. Preparare esempi concreti di implementazione per ogni argomento aumenta significativamente le probabilità di successo.

Middleware in ASP.NET Core

Il middleware rappresenta uno dei concetti fondamentali di ASP.NET Core. Ogni componente middleware elabora le richieste HTTP in sequenza, formando una pipeline che determina il comportamento dell'applicazione.

1. Qual è l'ordine corretto dei middleware nella pipeline?

L'ordine di registrazione dei middleware influenza direttamente il comportamento dell'applicazione. Un ordine errato può causare vulnerabilità di sicurezza o comportamenti inattesi.

Program.cs — Middleware order matterscsharp
var app = builder.Build();

app.UseExceptionHandler("/error");   // 1. Outermost: catches all exceptions
app.UseHsts();                        // 2. HTTP Strict Transport Security
app.UseHttpsRedirection();            // 3. Redirect HTTP to HTTPS
app.UseStaticFiles();                 // 4. Serve static files (short-circuits if found)
app.UseRouting();                     // 5. Match request to endpoint
app.UseAuthentication();              // 6. Identify the user
app.UseAuthorization();               // 7. Check permissions
app.MapControllers();                 // 8. Execute the matched endpoint

app.Run();

Il middleware di gestione delle eccezioni deve essere posizionato all'inizio della pipeline per catturare qualsiasi errore generato dai componenti successivi. L'autenticazione precede sempre l'autorizzazione, poiché non è possibile verificare i permessi di un utente non identificato.

2. Come si implementa un middleware personalizzato?

La creazione di middleware personalizzati permette di aggiungere funzionalità trasversali all'applicazione. Un esempio comune è il middleware per la misurazione dei tempi di risposta.

RequestTimingMiddleware.cscsharp
public class RequestTimingMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<RequestTimingMiddleware> _logger;

    public RequestTimingMiddleware(RequestDelegate next, ILogger<RequestTimingMiddleware> logger)
    {
        _next = next;
        _logger = logger;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        var stopwatch = Stopwatch.StartNew();
        
        await _next(context);  // Call the next middleware
        
        stopwatch.Stop();
        _logger.LogInformation(
            "Request {Method} {Path} completed in {Elapsed}ms",
            context.Request.Method,
            context.Request.Path,
            stopwatch.ElapsedMilliseconds);
    }
}

// Registration in Program.cs
app.UseMiddleware<RequestTimingMiddleware>();

Il costruttore riceve il delegato RequestDelegate che rappresenta il prossimo middleware nella catena. Il metodo InvokeAsync esegue la logica personalizzata prima e dopo la chiamata al middleware successivo.

3. Qual è la differenza tra Use, Run e Map?

Questi tre metodi di estensione servono scopi differenti nella configurazione della pipeline:

  • Use: aggiunge un middleware che può passare il controllo al successivo
  • Run: termina la pipeline, nessun middleware successivo viene eseguito
  • Map: crea una biforcazione della pipeline basata sul percorso della richiesta

4. Come si gestiscono le eccezioni globalmente tramite middleware?

Il middleware UseExceptionHandler cattura tutte le eccezioni non gestite e permette di restituire risposte consistenti. In ambiente di sviluppo, UseDeveloperExceptionPage fornisce informazioni dettagliate per il debugging.

5. Quando conviene usare middleware inline vs classi dedicate?

I middleware inline (lambda) sono appropriati per logiche semplici e specifiche. Le classi dedicate offrono maggiore testabilità, riutilizzabilità e supporto per la dependency injection nel costruttore.

6. Come funziona lo short-circuiting nella pipeline?

Lo short-circuiting si verifica quando un middleware decide di non invocare il componente successivo nella pipeline. UseStaticFiles è un esempio tipico: quando viene trovato un file statico corrispondente, la risposta viene restituita immediatamente senza coinvolgere autenticazione, autorizzazione o routing.

7. Come si testa un middleware personalizzato?

I middleware possono essere testati isolatamente creando un HttpContext fittizio e verificando il comportamento atteso.

RequestTimingMiddlewareTests.cscsharp
[Fact]
public async Task Middleware_LogsElapsedTime()
{
    var logger = new FakeLogger<RequestTimingMiddleware>();
    var middleware = new RequestTimingMiddleware(
        next: (ctx) => Task.CompletedTask,  // Terminal delegate
        logger: logger);

    var context = new DefaultHttpContext();
    context.Request.Method = "GET";
    context.Request.Path = "/api/products";

    await middleware.InvokeAsync(context);

    Assert.Single(logger.LogEntries);
    Assert.Contains("completed in", logger.LogEntries[0].Message);
}

L'uso di implementazioni fake per le dipendenze permette di verificare il comportamento senza dipendenze esterne.

Dependency Injection in .NET

La dependency injection rappresenta un pattern architetturale fondamentale in ASP.NET Core. Il container IoC integrato gestisce automaticamente il ciclo di vita delle dipendenze.

8. Quali sono i tre lifetime disponibili e quando usarli?

ASP.NET Core offre tre lifetime per i servizi registrati:

  • Transient: nuova istanza per ogni richiesta al container, ideale per servizi leggeri e stateless
  • Scoped: una istanza per richiesta HTTP, perfetto per DbContext e servizi che mantengono stato durante la richiesta
  • Singleton: una sola istanza per l'intera durata dell'applicazione, adatto per servizi di configurazione e cache

9. Cosa sono le captive dependencies e come si prevengono?

Una captive dependency si verifica quando un servizio con lifetime più lungo dipende da uno con lifetime più breve. Questo causa comportamenti imprevedibili poiché il servizio catturato vive oltre il suo ciclo di vita previsto.

Program.cs — Captive dependency detectioncsharp
builder.Services.AddScoped<IUserRepository, UserRepository>();
builder.Services.AddSingleton<ICacheService, CacheService>(); // CacheService takes IUserRepository

// This throws at startup in Development when ValidateScopes is enabled:
builder.Host.UseDefaultServiceProvider(options =>
{
    options.ValidateScopes = true;    // Catches captive dependencies
    options.ValidateOnBuild = true;   // Validates all registrations at startup
});

L'abilitazione di ValidateScopes e ValidateOnBuild in ambiente di sviluppo permette di identificare questi problemi durante l'avvio dell'applicazione.

10. Come funzionano i keyed services introdotti in .NET 8?

I keyed services permettono di registrare multiple implementazioni della stessa interfaccia, distinguendole tramite una chiave.

Program.cs — Registering keyed servicescsharp
builder.Services.AddKeyedSingleton<INotificationService, EmailNotification>("email");
builder.Services.AddKeyedSingleton<INotificationService, SmsNotification>("sms");
builder.Services.AddKeyedSingleton<INotificationService, PushNotification>("push");

// Resolving in a controller or service
public class OrderService
{
    public OrderService(
        [FromKeyedServices("email")] INotificationService emailService,
        [FromKeyedServices("sms")] INotificationService smsService)
    {
        // Each parameter gets its specific implementation
    }
}

Questa funzionalità elimina la necessità di factory pattern o service locator in molti scenari comuni.

11. Come si implementa il Decorator Pattern con DI?

Il Decorator Pattern permette di aggiungere comportamenti a un servizio senza modificarne l'implementazione originale. Un caso d'uso comune è l'aggiunta di caching.

csharp
// Decorating IProductService with caching
builder.Services.AddScoped<ProductService>();
builder.Services.AddScoped<IProductService>(sp =>
{
    var inner = sp.GetRequiredService<ProductService>();
    var cache = sp.GetRequiredService<IMemoryCache>();
    return new CachedProductService(inner, cache);
});

Pronto a superare i tuoi colloqui su .NET?

Pratica con i nostri simulatori interattivi, flashcards e test tecnici.

12. Qual è la differenza tra AddScoped e AddDbContext?

AddDbContext è un metodo specializzato per Entity Framework Core che registra il DbContext come Scoped e configura automaticamente le opzioni di connessione. Offre funzionalità aggiuntive come il pooling delle connessioni.

13. Come si gestisce il dispose dei servizi?

Il container DI chiama automaticamente Dispose o DisposeAsync sui servizi che implementano IDisposable o IAsyncDisposable quando il loro scope termina. I servizi Singleton vengono disposed allo shutdown dell'applicazione.

14. Quando utilizzare IServiceScopeFactory?

IServiceScopeFactory è necessario quando servizi Scoped devono essere risolti all'interno di contesti Singleton, come nei Background Services. Permette di creare un nuovo scope all'interno del quale i servizi Scoped funzionano correttamente.

Minimal APIs in ASP.NET Core

Le Minimal APIs rappresentano un approccio semplificato per la creazione di endpoint HTTP, introdotto in .NET 6 e continuamente migliorato nelle versioni successive.

15. Cosa sono le Minimal APIs e quando conviene utilizzarle?

Le Minimal APIs offrono un modello semplificato per la definizione di endpoint HTTP senza classi Controller. Sono ideali per microservizi, API semplici e prototipi. Per applicazioni complesse con logica di business articolata, i Controller offrono migliori possibilità di strutturazione.

16. Come si organizzano le Minimal APIs per scalabilità?

L'organizzazione in gruppi di endpoint e file separati mantiene il codice manutenibile anche in applicazioni complesse.

Features/Products/ProductEndpoints.cscsharp
public static class ProductEndpoints
{
    public static void MapProductEndpoints(this WebApplication app)
    {
        var group = app.MapGroup("/api/products")
            .WithTags("Products")
            .RequireAuthorization();

        group.MapGet("/", GetAll);
        group.MapGet("/{id:int}", GetById);
        group.MapPost("/", Create);
    }

    private static async Task<IResult> GetAll(
        IProductService productService,    // Auto-resolved from DI
        CancellationToken cancellationToken)
    {
        var products = await productService.GetAllAsync(cancellationToken);
        return TypedResults.Ok(products);
    }

    private static async Task<IResult> GetById(
        int id,
        IProductService productService)
    {
        var product = await productService.GetByIdAsync(id);
        return product is null
            ? TypedResults.NotFound()
            : TypedResults.Ok(product);
    }

    private static async Task<IResult> Create(
        CreateProductRequest request,
        IProductService productService)
    {
        var product = await productService.CreateAsync(request);
        return TypedResults.Created($"/api/products/{product.Id}", product);
    }
}

// Program.cs — Clean registration
app.MapProductEndpoints();

L'uso di MapGroup permette di applicare filtri, autorizzazione e metadata a tutti gli endpoint del gruppo contemporaneamente.

17. Qual è la differenza tra Results e TypedResults?

TypedResults fornisce tipizzazione forte per i risultati, migliorando l'integrazione con OpenAPI e permettendo al compilatore di verificare i tipi di ritorno. Results offre maggiore flessibilità ma meno garanzie a compile-time.

18. Come si implementa la validazione nelle Minimal APIs?

La validazione può essere implementata tramite filtri endpoint, librerie come FluentValidation o manualmente nel handler. I filtri endpoint offrono un approccio centralizzato e riutilizzabile.

19. Quali sono i vantaggi delle Minimal APIs rispetto ai Controller?

Le Minimal APIs offrono:

  • Minore overhead e migliori performance
  • Codice più conciso e leggibile
  • Supporto nativo per AOT compilation
  • Ideali per microservizi e API semplici

I Controller rimangono preferibili per applicazioni complesse con molte convenzioni condivise.

20. Come si gestisce la documentazione OpenAPI?

Le Minimal APIs supportano nativamente la generazione di documentazione OpenAPI tramite WithOpenApi(), WithDescription(), WithSummary() e attributi sui parametri.

Domande Avanzate di Architettura

21. Come interagiscono middleware e Minimal APIs?

Il middleware viene eseguito prima degli endpoint Minimal API. La registrazione tramite MapGet o MapPost crea middleware terminale che conclude la pipeline per le richieste corrispondenti. Tutta la middleware registrata viene attraversata prima di raggiungere l'endpoint.

22. Come si implementa il circuit breaker pattern in ASP.NET Core?

L'uso di Polly con IHttpClientFactory permette di implementare resilienza nelle chiamate HTTP, proteggendo l'applicazione da servizi esterni non disponibili.

23. Qual è il ruolo di IOptions, IOptionsSnapshot e IOptionsMonitor?

Questi pattern forniscono accesso tipizzato alla configurazione con diversi comportamenti di aggiornamento:

  • IOptions: configurazione statica, letta una volta all'avvio
  • IOptionsSnapshot: aggiornata per ogni richiesta Scoped
  • IOptionsMonitor: notifica le modifiche in tempo reale

24. Come si implementa il health check pattern?

I health checks permettono di monitorare lo stato dell'applicazione e delle sue dipendenze, fondamentali per orchestratori come Kubernetes. Vengono registrati con AddHealthChecks ed esposti come endpoint con MapHealthChecks.

25. Come si testano le Minimal APIs con WebApplicationFactory?

WebApplicationFactory<Program> permette di creare un server di test in-memory per test di integrazione completi, verificando l'intera pipeline dell'applicazione inclusi middleware, DI e routing.

Preparazione avanzata

La conoscenza dei pattern di testing dimostra maturità professionale. Durante i colloqui, illustrare la differenza tra test unitari, di integrazione e end-to-end applicati a middleware e API aumenta significativamente il valore percepito del candidato.

Conclusione

La preparazione per i colloqui ASP.NET Core richiede una comprensione solida dei concetti fondamentali e la capacità di applicarli in contesti reali. I punti chiave da ricordare includono:

  • L'ordine dei middleware nella pipeline influenza sicurezza e comportamento dell'applicazione
  • La scelta corretta del lifetime delle dipendenze previene memory leak e comportamenti anomali
  • I keyed services semplificano la gestione di multiple implementazioni
  • Le Minimal APIs offrono performance superiori per scenari appropriati
  • La testabilità deve guidare le decisioni architetturali

La pratica costante con questi concetti, combinata con la comprensione delle motivazioni dietro ogni scelta tecnica, prepara efficacemente per affrontare qualsiasi domanda di colloquio su ASP.NET Core.

Per consolidare queste competenze, il simulatore di colloqui .NET di SharpSkill offre esercitazioni pratiche su Minimal APIs, sviluppo Web API e clean architecture.

Inizia a praticare!

Metti alla prova le tue conoscenze con i nostri simulatori di colloquio e test tecnici.

Tag

#asp.net core
#interview
#middleware
#dependency injection
#minimal apis
#.net

Condividi

Articoli correlati