Gestire le richieste con controller in ASP.NET Core MVC

Steve Smith e Scott Addie

I controller, le azioni e risultati delle azioni sono parti fondamentali dello sviluppo di app tramite ASP.NET Core MVC.

Che cos'è un controller?

Un controller viene usato per definire e raggruppare un set di azioni. Un'azione (o metodo di azione) è un metodo in un controller che gestisce richieste. I controller raggruppano azioni simili in modo logico. Questa aggregazione di azioni consente l'applicazione collettiva di set di regole comuni, ad esempio routing, memorizzazione nella cache e autorizzazione. Le richieste vengono mappate alle azioni tramite routing. I controller vengono attivati ed eliminati per ogni richiesta.

Per convenzione, le classi controller:

  • Risiedere nella cartella Controller a livello radice del progetto.
  • Ereditare da Microsoft.AspNetCore.Mvc.Controller.

Un controller è una classe di cui è possibile creare un'istanza, in genere pubblica, in cui almeno una delle condizioni seguenti è vera:

  • Il nome della classe è suffisso con Controller.
  • La classe eredita da una classe il cui nome è suffisso con Controller.
  • L'attributo [Controller] viene applicato alla classe .

A una classe controller non deve essere associato un attributo [NonController].

I controller devono seguire il principio delle dipendenze esplicite. Per l'implementazione di questo principio esistono due approcci. Se più azioni del controller richiedono lo stesso servizio, prendere in considerazione l'uso dell'inserimento di costruttori per richiedere tali dipendenze. Se il servizio è richiesto da un solo metodo di azione, prendere in considerazione l'uso dell'inserimento di azioni per richiedere la dipendenza.

All'interno del modello Model-View-Controller, un controller è responsabile dell'elaborazione iniziale della richiesta e della creazione di istanze del modello. In genere, per le decisioni aziendali è consigliabile seguire il modello.

Il controller riceve il risultato di un'eventuale elaborazione del modello e restituisce la visualizzazione corretta e i dati associati oppure il risultato della chiamata API. Per altre informazioni, vedere Panoramica di ASP.NET Core MVC e Introduzione ad ASP.NET Core MVC e Visual Studio.

Il controller è un'astrazione a livello di interfaccia utente. Il suo compito è di verificare che i dati della richiesta siano validi e di scegliere la visualizzazione (o il risultato per un'API) da restituire. Nelle app con factoring corretto, il controller non include direttamente accesso ai dati o logica di business, ma delega la gestione di tali responsabilità a servizi specifici.

Definizione di azioni

I metodi pubblici in un controller, ad eccezione di quelli con l'attributo [NonAction] , sono azioni. I parametri delle azioni sono associati a dati di richiesta e vengono convalidati tramite associazione di modelli. La convalida del modello viene eseguita per tutto ciò che è associato a un modello. Il valore della proprietà ModelState.IsValid indica se l'associazione e la convalida dei modelli hanno avuto esito positivo.

I metodi di azione devono contenere la logica per il mapping di una richiesta a un problema di business. È di solito consigliabile rappresentare i problemi di business come servizi a cui il controller accede tramite inserimento di dipendenze. Le azioni eseguono quindi il mapping del risultato dell'azione di business a uno stato dell'applicazione.

Le azioni possono restituire qualsiasi valore, ma spesso restituiscono un'istanza di IActionResult (o di Task<IActionResult> per i metodi asincroni) che genera una risposta. Il metodo di azione è responsabile della scelta del tipo di risposta. Il risultato dell'azione esegue la risposta.

Metodi helper dei controller

I controller ereditano in genere da Controller, anche se questo non è obbligatorio. La derivazione da Controller consente l'accesso a tre categorie di metodi helper:

1. Metodi che generano un corpo di risposta vuoto

Non è inclusa un'intestazione di risposta HTTP Content-Type, poiché il corpo della risposta non ha contenuto da descrivere.

All'interno di questa categoria sono presenti due tipi di risultati: reindirizzamento e codice di stato HTTP.

  • Codice di stato HTTP

    Questo tipo restituisce un codice di stato HTTP. Alcuni metodi helper di questo tipo sono BadRequest, NotFound e Ok. Il metodo return BadRequest();, ad esempio, quando viene eseguito genera un codice di stato 400. Quando metodi come BadRequest, NotFound e Ok vengono sottoposti a overload, non sono più risponditori del codice di stato HTTP, poiché è in corso la negoziazione del contenuto.

  • Reindirizzamento

    Questo tipo restituisce un reindirizzamento a un'azione o a una destinazione (tramite Redirect, LocalRedirect, RedirectToAction o RedirectToRoute). return RedirectToAction("Complete", new {id = 123});, ad esempio, reindirizza a Complete, passando un oggetto anonimo.

    Il tipo di risultato del reindirizzamento è diverso dal tipo del codice di stato HTTP principalmente per l'aggiunta di una intestazione della risposta HTTP Location.

2. Metodi che generano un corpo della risposta non vuoto con un tipo di contenuto predefinito

La maggior parte dei metodi helper di questa categoria includono una proprietà ContentType, che consente di impostare l'intestazione della risposta Content-Type in modo da descrivere il corpo della risposta.

All'interno di questa categoria sono presenti due tipi di risultato: visualizzazione e risposta formattata.

  • Visualizza

    Questo tipo restituisce una visualizzazione che esegue il rendering HTML usando un modello. return View(customer);, ad esempio, passa un modello alla visualizzazione per eseguire il data binding.

  • Risposta formattata

    Questo tipo restituisce JSON o un formato di scambio di dati simile per rappresentare un oggetto in modo specifico. Ad esempio, return Json(customer); serializza l'oggetto fornito in JSformato ON.

    Altri metodi comuni di questo tipo sono File e PhysicalFile. Ad esempio return PhysicalFile(customerFilePath, "text/xml"); restituisce PhysicalFileResult.

3. Metodi che generano un corpo della risposta non vuoto formattato in un tipo di contenuto negoziato con il client

Questa categoria è più nota come negoziazione del contenuto. La negoziazione del contenuto viene applicata ogni volta che un'azione restituisce un ObjectResult tipo o un elemento diverso da un'implementazione IActionResult . Anche un'azione che restituisce un'implementazione non di IActionResult (ad esempio, object) restituisce una risposta formattata.

Alcuni metodi helper di questo tipo sono BadRequest, CreatedAtRoute e Ok. Alcuni esempi di questi metodi sono, rispettivamente, return BadRequest(modelState);, return CreatedAtRoute("routename", values, newobject); e return Ok(value);. Si noti che BadRequest e Ok eseguono la negoziazione del contenuto solo quando viene passato loro un valore. Se non viene loro passato alcun valore, fungono invece da tipi di risultato codice di stato HTTP. Il metodo CreatedAtRoute, d'altra parte, esegue sempre la negoziazione del contenuto, perché tutti gli overload di questo metodo richiedono che venga passato un valore.

Problemi di montaggio incrociato

Le applicazioni condividono in genere parti del flusso di lavoro. Tra gli esempi possibili, un'app che richiede l'autenticazione per accedere al carrello o un'app che memorizza nella cache i dati di alcune pagine. Per eseguire della logica prima o dopo un metodo di azione, usare un filtro. L'uso di filtri su problemi di montaggio incrociato può ridurre la duplicazione.

La maggior parte degli attributi di filtro, ad esempio [Authorize], può essere applicata a livello di controller o di azione, a seconda del livello di granularità desiderato.

La gestione degli errori e la memorizzazione nella cache delle risposte rappresentano spesso problemi di montaggio incrociato:

Molti problemi di montaggio incrociato possono essere gestiti tramite filtri o middleware personalizzato.