Il presente articolo è stato tradotto automaticamente.

Cutting Edge

Filtri azione in ASP.NET MVC

Dino Esposito

La sfida più grande che molti progettisti di software devono affrontare oggi consiste nel progettare e implementare un'applicazione che può soddisfare tutti i requisiti di versione 1 e tutti gli altri utenti può visualizzare successivamente. Gestibilità è stato uno degli attributi fondamentali della progettazione di software dopo la prima bozza del documento ISO/IEC 9126 nel 1991. (Il documento fornisce una descrizione formale della qualità del software e suddivide in un insieme di caratteristiche e sub-characteristics, uno dei quali è la facilità di gestione. È possibile ottenere una versione PDF della carta in iso.org .)

La possibilità di soddisfare esigenze presenti e future dei clienti non è certamente un nuovo requisito per qualsiasi componente software. Tuttavia, quali molte applicazioni Web richiedono oggi è una forma sottile e a breve termine di gestibilità. I clienti più volte per evitare nuove funzioni o una diversa implementazione di una funzione esistente. Essi desiderano semplicemente aggiungere, sostituire, configurare o rimuovere piccole parti di funzionalità. Un esempio tipico è quando siti Web con elevati audience avviare campagne pubblicitarie specifiche. Non cambia il comportamento generale del sito, ma con azioni esistenti, è necessario eseguire azioni aggiuntive. Inoltre, tali modifiche non in genere permanente. È necessario rimuoverli dopo poche settimane per essere quindi reintegrati pochi mesi dopo, configurato in modo diverso e così via. È necessario programmare le funzionalità richieste da composizione piccole parti contemporaneamente;è necessario tenere traccia delle dipendenze senza influire notevolmente sulle codice sorgente;e si desidera spostare verso un aspetto orientati del software. Questi sono alcuni dei principali motivi dietro la rapida diffusione di infrastrutture di inversione del controllo (IoC) in molti progetti dell'organizzazione.

Cos'è dunque su questo articolo? Non è destinato a essere una lezione interessante sulla modalità di modifica oggi software. È invece un'esplorazione approfondita di una potente funzionalità di ASP.NET MVC controller soprattutto utili nella creazione di soluzioni Web basata su aspetti: Filtri di azione di ASP.NET MVC.

Ciò che un'operazione filtro, comunque?

Un filtro di azione è un attributo che, quando viene associato a una classe controller o a un metodo controller, fornisce in modo dichiarativo per associare un comportamento a un'azione richiesta. Scrivendo un filtro azione è possibile agganciare la pipeline di esecuzione di un metodo di azione e adattare alle proprie esigenze. In questo modo si può accettare anche all'esterno della classe controller qualsiasi logica non appartiene esclusivamente al controller. In questo modo, si apportano questo specifico comportamento riutilizzabili e, soprattutto, facoltativo. Filtri di azione sono ideali per implementare crosscutting problemi che influiscono sulla vita dei controller.

ASP.NET MVC viene fornito con alcuni filtri di azione predefiniti quali HandleError e autorizza OutputCache. È possibile utilizzare HandleError per intercettare le eccezioni generate durante l'esecuzione dei metodi della classe controller di destinazione. L'interfaccia di programmazione dell'attributo HandleError consente di indicare la visualizzazione di errori per associare un tipo di eccezione specificata.

L'attributo autorizza blocca l'esecuzione di un metodo per gli utenti non autorizzati. Non distingue, tuttavia gli utenti che semplicemente non sono state ancora l'accesso e l'accesso agli utenti privi di autorizzazioni sufficienti per eseguire una determinata azione. Nella configurazione dell'attributo, è possibile specificare i ruoli necessari per eseguire una determinata azione.

L'attributo OutputCache memorizza nella cache la risposta del metodo del controller per la durata specificata e richiesto elenco di parametri.

Una classe di azione filtro implementa interfacce diverse. Di Figura 1 viene presentato l'elenco completo delle interfacce.

Figura 1 di interfacce per un'azione filtro

Filtrare le interfacce Descrizione
IActionFilter Prima e dopo l'esecuzione del metodo controller, vengono richiamati i metodi nell'interfaccia.
IAuthorizationFilter Prima di eseguita il metodo controller, vengono richiamati i metodi nell'interfaccia.
IExceptionFilter Ogni volta che viene generata un'eccezione durante l'esecuzione del metodo controller, vengono richiamati i metodi nell'interfaccia.
IResultFilter Prima e dopo l'elaborazione dei risultati dell'operazione, vengono richiamati i metodi nell'interfaccia.

In genere, si è prevalentemente IActionFilter e IResultFilter. Ottenere Let’s li conoscono meglio. Ecco la definizione dell'interfaccia IActionFilter:

public interface IActionFilter
{
  void OnActionExecuted(ActionExecutedContext filterContext);
  void OnActionExecuting(ActionExecutingContext filterContext);
}

Implementare il metodo OnActionExecuting per eseguire il codice prima dell'esecuzione azione del controller del;implementare OnActionExecuted post-process stato controller che ha determinato un metodo. Gli oggetti contesto forniscono numerose informazioni di runtime. Questa è la firma di ActionExecutingContext:

public class ActionExecutingContext : ControllerContext
{
  public ActionDescriptor ActionDescriptor { get; set; }
  public ActionResult Result { get; set; }
  public IDictionary<string, object> ActionParameters { get; set; }
}

Descrittore di azione, in particolare, fornisce informazioni sul metodo di azione, ad esempio nome, controller, parametri, attributi e altri filtri. La firma di ActionExecutedContext è solo leggermente diversa, come illustrato di seguito:

public class ActionExecutedContext : ControllerContext
{
  public ActionDescriptor ActionDescriptor { get; set; }
  public ActionResult Result { get; set; }
  public bool Canceled { get; set; }
  public Exception Exception { get; set; }
  public bool ExceptionHandled { get; set; }
}

Oltre a un riferimento alla descrizione azione e il risultato dell'azione, la classe fornisce informazioni su un'eccezione che potrebbe già verificati e offre due flag booleani che meritano attenzione più. Il flag ExceptionHandled indica che il filtro di azione viene assegnato possibilità di contrassegnare un'eccezione che si è verificata come gestito. Il flag annullato riguarda la proprietà Result della classe ActionExecutingContext.

Si noti che la proprietà Result della classe ActionExecutingContext esiste solo per spostare il carico di generare qualsiasi risposta azione dal metodo controller all'elenco dei filtri di azione registrata. Se i filtri azione assegnano un valore alla proprietà Result, il metodo di destinazione della classe controller non verrà richiamato. In questo modo, si ignora il metodo di destinazione, spostando il carico di produzione di una risposta interamente ai filtri di azione. Tuttavia, se sono stati registrati più filtri di azione per un metodo controller, questi sarebbe condividono il risultato dell'azione. Quando un filtro imposta il risultato dell'operazione, tutti i filtri successivi nella catena riceverà l'oggetto ActionExecuteContext annullato la proprietà è impostata su true. Se si imposta a livello di programmazione annullato nel passaggio azione eseguita oppure la proprietà Result nel passaggio esecuzione azione, il metodo di destinazione non verrà eseguito.

Filtri di operazione di scrittura

Come accennato, quando si tratta di scrivere filtri personalizzati, la maggior parte del tempo si interessano i filtri di pre e post-process i filtri che vengono eseguiti prima e dopo l'esecuzione del metodo controller regolari e risultato dell'azione. Invece di implementare le interfacce in modalità nativa, un'azione filtro è comunemente derivata da ActionFilterAttribute:

public abstract class ActionFilterAttribute : FilterAttribute, IActionFilter, IResultFilter
{
  public virtual void OnActionExecuted(ActionExecutedContext filterContext);
  public virtual void OnActionExecuting(ActionExecutingContext filterContext);
  public virtual void OnResultExecuted(ResultExecutedContext filterContext);
  public virtual void OnResultExecuting(ResultExecutingContext filterContext);
}

Si esegue l'override di OnActionExecuted aggiungere codice personalizzato per l'esecuzione del metodo. È possibile ignorare OnActionExecuting come condizione preliminare per l'esecuzione del metodo di destinazione. Infine, è possibile ignorare OnResultExecuting e OnResultExecuted per inserire il codice intorno al passaggio interno che determina la generazione di risposta al metodo.

Figura 2 viene illustrato un esempio di filtro azione aggiunge a livello di programmazione alla risposta del metodo a che viene applicato compressione.

Figura 2 di un filtro di azione di esempio per la compressione della risposta del metodo

public class CompressAttribute : ActionFilterAttribute
{
  public override void OnActionExecuting(
    ActionExecutingContext filterContext)
  {
    // Analyze the list of acceptable encodings
    var preferred = GetPreferredEncoding(
      filterContext.HttpContext.Request);

    // Compress the response accordingly
    var response = filterContext.HttpContext.Response;
    response.AppendHeader("Content-encoding", preferred.ToString());

    if (preferredEncoding == CompressionScheme.Gzip)
    {
      response.Filter = new GZipStream(
        response.Filter, CompressionMode.Compress);
    }

    if (preferredEncoding == CompressionScheme.Deflate)
    {
      response.Filter = new DeflateStream(response.Filter, CompressionMode.Compress);
    }
    return;
  }

  static CompressionScheme GetPreferredEncoding(HttpRequestBase request)
  {
    var acceptableEncoding = request.Headers["Accept-Encoding"];
    acceptableEncoding = SortEncodings(acceptableEncoding);

    // Get the preferred encoding format 
    if (acceptableEncoding.Contains("gzip"))
      return CompressionScheme.Gzip;
    if (acceptableEncoding.Contains("deflate"))
      return CompressionScheme.Deflate;

    return CompressionScheme.Identity;
  }

  static String SortEncodings(string header)
  {
    // Omitted for brevity
  }
}

In ASP.NET, compressione comunemente viene ottenuta tramite la registrazione di un modulo HTTP che intercetta le richieste e comprime le risposte. In alternativa, è anche possibile abilitare la compressione a livello di IIS. ASP.NET MVC supporta anche entrambi gli scenari e anche una terza offerte opzione: compressione in base al metodo di controllo. In questo modo è possibile controllare il livello di compressione per un URL specifico senza la necessità di scrivere, registrazione e gestione di un modulo HTTP.

Come può vedere in di Figura 2, il filtro di azione sovrascrive il metodo OnActionExecuting. Questo può sembrare un po' strana all'inizio, come si può immaginare la compressione per essere una preoccupazione crosscutting occupano di prima di restituire alcuni risposta. Compressione viene implementato tramite la proprietà Filter dell'HttpResponse intrinseco. Qualsiasi risposta fase dall'ambiente di runtime viene restituita al browser client mediante l'oggetto HttpResponse. Successivamente, i flussi personalizzati montati sul flusso di output predefinito tramite la proprietà Filter possono alterare l'output inviato. Pertanto, nel corso del metodo OnActionExecuting è semplicemente impostare ulteriori flussi in cima al flusso di output predefinito.

Per quanto riguarda la compressione HTTP, tuttavia, la parte più difficile è considerando attenzione le preferenze del browser. Il browser invia le preferenze di compressione attraverso l'intestazione Accept-Encoding. Il contenuto dell'intestazione indica che il browser è in grado di gestire solo determinate codifiche, in genere gzip e deflate. Per essere un buon cittadino, il filtro di azione deve tentare di capire esattamente ciò che il browser può gestire. Può essere un'attività complessa. Il ruolo dell'intestazione Accept-Encoding completamente è descritto nella RFC 2616 (vedere w3.org/Protocols/rfc2616/rfc2616-sec14.html ). In brief, the content of the Accept-Encoding header can contain a q parameter that’s meant to assign a priority to an acceptable value. Considerare ad esempio le stringhe seguenti sono valori validi per una codifica, ma anche se gzip è evidentemente la prima scelta in tutte, solo nel primo sono la scelta preferita:

gzip, deflate
gzip;q=.7,deflate
gzip;q=.5,deflate;q=.5,identity

Un filtro di compressione deve prendere in considerazione, come il filtro in di Figura 2. Questi dettagli dovrebbero rafforzare l'idea quando si scrive un filtro di azione, sta interferire con la gestione della richiesta. Successivamente, qualsiasi che scopo devono essere coerenti con la previsione del browser client.

Applicazione di un filtro di azione

Come accennato, un filtro di azione è semplicemente un attributo può essere applicato a singoli metodi anche per la classe padre intero. Ecco come dovrebbe impostare:

[Compress]
public ActionResult List()
{
  // Some code here
  ...
}

Se la classe di attributo contiene alcune proprietà pubbliche, è possibile assegnare valori ad essi in modo dichiarativo utilizzando la notazione di attributi comuni:

[Compress(Level=1)]
public ActionResult List()
{
  ...
}

Figura 3 indica la risposta compressa segnalata da Firebug.

image: A Compressed Response Obtained via the Compress Attribute

Figura 3 A risposte compresse ottenuta tramite l'attributo di compressione

Un attributo è ancora di un metodo in modo statico. Ciò significa che è necessario un secondo passaggio di compilazione per applicare ulteriori modifiche. Filtri di azione espressi sotto forma di attributi, tuttavia, forniscono un vantaggio: Mantenere crosscutting preoccupazioni out del metodo azione principale.

Un'analisi più ampia azione filtri

Per misurare la vera potenza dei filtri di azione, considerare le applicazioni che devono molto personalizzazione funziona nel tempo e le applicazioni che richiedono adattamento installato per diversi clienti.

Si supponga, ad esempio, un sito Web che a un certo punto avvia una campagna pubblicitaria in base ai punti ricompensa che potrebbe ottenere un utente registrato se si esegue un'azione standard all'interno di un sito (acquisto di merci, rispondere alle domande, chat, blog e così via). Come sviluppatore, è probabilmente necessario del codice che viene eseguito dopo che è stato eseguito il metodo regolare dell'esecuzione di una transazione, un commento di registrazione o avviare una chat. Sfortunatamente, questo codice come passare e non dovrà far parte dell'implementazione del metodo azione principale originale. I filtri di azione, è possibile creare componenti distinti per ogni scenario necessario e disporre, ad esempio, un filtro di azione per l'aggiunta di punti ricompensa. Successivamente, il filtro ricompensa si collega a tutti i metodi in cui è necessario; l'azione di inserimentoquindi è necessario ricompilare e passare.

[Reward(Points=100)]
public ActionResult Post(String post)
{
  // Core logic for posting to a blog
  ...
}

Come accennato, gli attributi sono statici e richiedono un passaggio di compilazione aggiuntiva. Ciò può non essere auspicabile in tutti i casi (ad esempio, nei siti con funzionalità altamente volatile), è molto meglio di niente. Come minimo, è possibile ottenere la possibilità di aggiornare soluzioni Web rapidamente e con un impatto minimo sulle funzionalità esistenti, che è sempre buona evitare errori di regressione sotto stretto controllo.

Caricamento dinamico

In questo articolo viene dimostrato filtri azione nel contesto dei metodi azione controller. È stato illustrato l'approccio canonico di scrivere filtri come attributi consente di decorare metodi azione staticamente. C'è tuttavia una domanda sottostante: Possibile caricare filtri azione dinamicamente?

ASP.NET MVC framework è una porzione di codice, ben scritta (e grande) espone un numero di interfacce e metodi sottoponibili a override con cui è possibile personalizzare quasi ogni aspetto di esso. Fortunatamente, questa tendenza sta per essere rafforzato per l'imminente Model View Controller (MVC) 3. In base all'orientamento pubblica, uno degli obiettivi del team per MVC 3 è attivazione inserimento delle dipendenze a tutti i livelli. La risposta alla domanda precedente sul caricamento dinamico, pertanto consiste nella possibilità di inserimento delle dipendenze del framework MVC. Un possibile strategia di vincere Personalizza l'invoker operazione per accedere all'elenco dei filtri prima dell'esecuzione di un metodo. Poiché l'elenco di filtri è simile a un oggetto insieme di lettura/scrittura normale, non dovrebbe essere difficile da compilare in modo dinamico. Ma questa è una buona fodder per una nuova colonna.

Dino Esposito è autore di “ Programming ASP.NET MVC ” Microsoft Press (2010) ed è coautore “Microsoft. NET: Architettura delle applicazioni per l' organizzazione ” (Microsoft Press, 2008). Vive in Italia, Esposito è spesso come relatore a eventi del settore in tutto il mondo. È possibile unire il suo blog all'indirizzo indirizzo weblogs.asp.net/despos.

Grazie all'esperto di tecnica seguente per la revisione di questo articolo: Scott Hanselman