Filtri in ASP.NET Core

Di Kirk Larkin, Rick Anderson, Tom Dykstra e Steve Smith

I filtri in ASP.NET Core consentono l'esecuzione del codice prima o dopo fasi specifiche nella pipeline di elaborazione delle richieste.

I filtri predefiniti gestiscono attività, ad esempio:

  • Autorizzazione, che impedisce l'accesso alle risorse per cui un utente non è autorizzato.
  • Memorizzazione nella cache delle risposte, corto circuito della pipeline di richiesta per restituire una risposta memorizzata nella cache.

I filtri personalizzati possono essere creati per gestire problemi relativi a più settori. Esempi di problematiche trasversali includono la gestione degli errori, la memorizzazione nella cache, la configurazione, l'autorizzazione e la registrazione. I filtri evitano la duplicazione del codice. Ad esempio, un filtro eccezioni per la gestione degli errori potrebbe consolidare la gestione degli errori.

Questo documento si applica a Razor Pagine, controller API e controller con visualizzazioni. I filtri non funzionano direttamente con i Razor componenti. Un filtro può influire indirettamente su un componente quando:

  • Il componente è incorporato in una pagina o in una visualizzazione.
  • La pagina o il controller e la visualizzazione usano il filtro.

Funzionamento dei filtri

I filtri vengono eseguiti all'interno della pipeline di chiamata di azioni ASP.NET Core, talvolta definita pipeline di filtro. La pipeline di filtro viene eseguita dopo ASP.NET Core seleziona l'azione da eseguire:

The request is processed through Other Middleware, Routing Middleware, Action Selection, and the Action Invocation Pipeline. The request processing continues back through Action Selection, Routing Middleware, and various Other Middleware before becoming a response sent to the client.

Tipi di filtri

Ogni tipo di filtro viene eseguito in una fase diversa della pipeline di filtro:

  • Filtri autorizzazione:

    • Eseguire prima di tutto.
    • Determinare se l'utente è autorizzato per la richiesta.
    • Corto circuito della pipeline se la richiesta non è autorizzata.
  • Filtri di risorse:

    • Vengono eseguiti dopo l'autorizzazione.
    • OnResourceExecuting esegue il codice prima del resto della pipeline del filtro. Ad esempio, OnResourceExecuting esegue il codice prima dell'associazione di modelli.
    • OnResourceExecuted esegue il codice dopo il completamento del resto della pipeline.
  • Filtri azione:

    • Eseguire immediatamente prima e dopo la chiamata di un metodo di azione.
    • Può modificare gli argomenti passati in un'azione.
    • Può modificare il risultato restituito dall'azione.
    • Non sono supportati in Razor Pages.
  • Filtri endpoint:

    • Eseguire immediatamente prima e dopo la chiamata di un metodo di azione.
    • Può modificare gli argomenti passati in un'azione.
    • Può modificare il risultato restituito dall'azione.
    • Non sono supportati in Razor Pages.
    • Può essere richiamato su entrambe le azioni e sugli endpoint basati sul gestore di route.
  • I filtri eccezioni applicano criteri globali a eccezioni non gestite che si verificano prima che il corpo della risposta sia stato scritto in.

  • Filtri dei risultati:

    • Eseguire immediatamente prima e dopo l'esecuzione dei risultati dell'azione.
    • Eseguire solo quando il metodo di azione viene eseguito correttamente.
    • Sono utili per la logica che deve racchiudere l'esecuzione della visualizzazione o del formattatore.

Il diagramma seguente mostra come interagiscono i tipi di filtro nella pipeline di filtro:

The request is processed through Authorization Filters, Resource Filters, Model Binding, Action Filters, Action Execution and Action Result Conversion, Exception Filters, Result Filters, and Result Execution. On the way out, the request is only processed by Result Filters and Resource Filters before becoming a response sent to the client.

Razor Le pagine supportano Razor anche i filtri pagina, che vengono eseguiti prima e dopo un Razor gestore di pagine.

Implementazione

I filtri supportano sia implementazioni sincrone che asincrone tramite definizioni di interfaccia diverse.

I filtri sincroni vengono eseguiti prima e dopo la fase della pipeline. La chiamata di OnActionExecuting, ad esempio, avviene prima della chiamata del metodo di azione. OnActionExecuted viene chiamato dopo che il metodo di azione restituisce:

public class SampleActionFilter : IActionFilter
{
    public void OnActionExecuting(ActionExecutingContext context)
    {
        // Do something before the action executes.
    }

    public void OnActionExecuted(ActionExecutedContext context)
    {
        // Do something after the action executes.
    }
}

I filtri asincroni definiscono un On-Stage-ExecutionAsync metodo. Ad esempio, OnActionExecutionAsync:

public class SampleAsyncActionFilter : IAsyncActionFilter
{
    public async Task OnActionExecutionAsync(
        ActionExecutingContext context, ActionExecutionDelegate next)
    {
        // Do something before the action executes.
        await next();
        // Do something after the action executes.
    }
}

Nel codice precedente l'oggetto SampleAsyncActionFilter ha un ActionExecutionDelegateoggetto , nextche esegue il metodo di azione.

Fasi di filtro multiple

È possibile implementare interfacce per più fasi di filtro in una singola classe. Ad esempio, la ActionFilterAttribute classe implementa:

Implementare la versione sincrona oppure la versione asincrona di un'interfaccia di filtro, non entrambe. Il runtime controlla per prima cosa se il filtro implementa l'interfaccia asincrona e, in tal caso, la chiama. In caso contrario, chiama i metodi dell'interfaccia sincrona. Se in una classe sono implementate entrambe le interfacce, sincrona e asincrona, viene chiamato solo il metodo asincrono. Quando si usano classi astratte come ActionFilterAttribute, eseguire l'override solo dei metodi sincroni o dei metodi asincroni per ogni tipo di filtro.

Attributi filtro predefiniti

ASP.NET Core include filtri predefiniti basati su attributi che è possibile impostare come sottoclasse e personalizzare. Il filtro dei risultati seguente, ad esempio, aggiunge un'intestazione alla risposta:

public class ResponseHeaderAttribute : ActionFilterAttribute
{
    private readonly string _name;
    private readonly string _value;

    public ResponseHeaderAttribute(string name, string value) =>
        (_name, _value) = (name, value);

    public override void OnResultExecuting(ResultExecutingContext context)
    {
        context.HttpContext.Response.Headers.Add(_name, _value);

        base.OnResultExecuting(context);
    }
}

Gli attributi consentono ai filtri di accettare argomenti, come illustrato nell'esempio precedente. Applicare ResponseHeaderAttribute a un controller o a un metodo di azione e specificare il nome e il valore dell'intestazione HTTP:

[ResponseHeader("Filter-Header", "Filter Value")]
public class ResponseHeaderController : ControllerBase
{
    public IActionResult Index() =>
        Content("Examine the response headers using the F12 developer tools.");

    // ...

Usare uno strumento come gli strumenti di sviluppo del browser per esaminare le intestazioni. In Intestazionifilter-header: Filter Value di risposta viene visualizzato.

Il codice seguente si applica ResponseHeaderAttribute sia a un controller che a un'azione:

[ResponseHeader("Filter-Header", "Filter Value")]
public class ResponseHeaderController : ControllerBase
{
    public IActionResult Index() =>
        Content("Examine the response headers using the F12 developer tools.");

    // ...

    [ResponseHeader("Another-Filter-Header", "Another Filter Value")]
    public IActionResult Multiple() =>
        Content("Examine the response headers using the F12 developer tools.");
}

Le risposte dell'azione Multiple includono le intestazioni seguenti:

  • filter-header: Filter Value
  • another-filter-header: Another Filter Value

Molte delle interfacce del filtro presentano attributi corrispondenti che possono essere usati come classi di base per implementazioni personalizzate.

Attributi dei filtri:

I filtri non possono essere applicati ai Razor metodi del gestore Page. Possono essere applicati al Razor modello Page o a livello globale.

Ambiti dei filtri e ordine di esecuzione

È possibile aggiungere un filtro alla pipeline in uno dei tre ambiti:

  • Uso di un attributo in un controller o Razor in una pagina.
  • Uso di un attributo in un'azione del controller. Gli attributi di filtro non possono essere applicati ai Razor metodi del gestore Pages.
  • A livello globale per tutti i controller, le azioni e Razor le pagine, come illustrato nel codice seguente:
    var builder = WebApplication.CreateBuilder(args);
    
    // Add services to the container.
    builder.Services.AddControllersWithViews(options =>
    {
        options.Filters.Add<GlobalSampleActionFilter>();
    });
    

Ordine di esecuzione predefinito

Quando sono presenti più filtri per una particolare fase della pipeline, l'ambito determina l'ordine di esecuzione predefinito dei filtri. I filtri globali racchiudono i filtri di classe, che a loro volta racchiudono i filtri dei metodi.

Come risultato dell'annidamento dei filtri, il codice after dei filtri viene eseguito in ordine inverso rispetto al codice before. Sequenza di filtro:

  • Codice before dei filtri globali.
    • Codice before dei filtri del controller.
      • Codice before dei filtri del metodo di azione.
      • Codice after dei filtri del metodo di azione.
    • Codice after dei filtri del controller.
  • Codice after dei filtri globali.

Nell'esempio seguente viene illustrato l'ordine in cui vengono eseguiti i metodi di filtro per i filtri di azione sincroni:

Sequence Ambito del filtro Metodo del filtro
1 Generale OnActionExecuting
2 Controller OnActionExecuting
3 Azione OnActionExecuting
4 Azione OnActionExecuted
5 Controller OnActionExecuted
6 Generale OnActionExecuted

Filtri a livello di controller

Ogni controller che eredita da Controller include i OnActionExecutingmetodi , OnActionExecutionAsynce OnActionExecuted . Questi metodi eseguono il wrapping dei filtri eseguiti per una determinata azione:

  • OnActionExecuting viene eseguito prima di uno dei filtri dell'azione.
  • OnActionExecuted viene eseguito dopo tutti i filtri dell'azione.
  • OnActionExecutionAsync viene eseguito prima di uno dei filtri dell'azione. Codice dopo l'esecuzione di una chiamata a next dopo i filtri dell'azione.

La classe seguente ControllerFiltersController :

  • Applica l'oggetto SampleActionFilterAttribute ([SampleActionFilter]) al controller.
  • Esegue l'override di OnActionExecuting e OnActionExecuted.
[SampleActionFilter]
public class ControllerFiltersController : Controller
{
    public override void OnActionExecuting(ActionExecutingContext context)
    {
        Console.WriteLine(
            $"- {nameof(ControllerFiltersController)}.{nameof(OnActionExecuting)}");

        base.OnActionExecuting(context);
    }

    public override void OnActionExecuted(ActionExecutedContext context)
    {
        Console.WriteLine(
            $"- {nameof(ControllerFiltersController)}.{nameof(OnActionExecuted)}");

        base.OnActionExecuted(context);
    }

    public IActionResult Index()
    {
        Console.WriteLine(
            $"- {nameof(ControllerFiltersController)}.{nameof(Index)}");

        return Content("Check the Console.");
    }
}

Passando a https://localhost:<port>/ControllerFilters, viene eseguito il codice seguente:

  • ControllerFiltersController.OnActionExecuting
    • GlobalSampleActionFilter.OnActionExecuting
      • SampleActionFilterAttribute.OnActionExecuting
        • ControllerFiltersController.Index
      • SampleActionFilterAttribute.OnActionExecuted
    • GlobalSampleActionFilter.OnActionExecuted
  • ControllerFiltersController.OnActionExecuted

I filtri a livello di controller impostano la proprietà Order su int.MinValue. Non è possibile impostare filtri a livello di controller per l'esecuzione dopo l'applicazione dei filtri ai metodi. L'ordine è illustrato nella sezione successiva.

Per Razor le pagine, vedere Implementare Razor filtri di pagina eseguendo l'override dei metodi di filtro.

Eseguire l'override dell'ordine predefinito

È possibile eseguire l'override della sequenza di esecuzione predefinita implementando IOrderedFilter. IOrderedFilter espone la proprietà Order, che ha la precedenza sull'ambito per determinare l'ordine di esecuzione. Un filtro con un valore di Order inferiore:

  • Esegue il codice before prima di quello di un filtro con un valore di Order più alto.
  • Esegue il codice after dopo quello di un filtro con un valore di Order più alto.

Nell'esempio di filtri a livello di controller ha GlobalSampleActionFilter un ambito globale in modo che venga eseguito prima SampleActionFilterAttributedi , con ambito controller. Per eseguire SampleActionFilterAttribute prima di tutto, impostarne l'ordine su int.MinValue:

[SampleActionFilter(Order = int.MinValue)]
public class ControllerFiltersController : Controller
{
    // ...
}

Per fare in modo che il filtro GlobalSampleActionFilter globale sia eseguito per primo, impostarlo Order su int.MinValue:

builder.Services.AddControllersWithViews(options =>
{
    options.Filters.Add<GlobalSampleActionFilter>(int.MinValue);
});

Annullamento e corto circuito

È possibile causare il corto circuito della pipeline di filtro impostando la proprietà Result sul parametro ResourceExecutingContext fornito al metodo di filtro. Ad esempio, il filtro risorsa seguente impedisce l'esecuzione del resto della pipeline:

public class ShortCircuitingResourceFilterAttribute : Attribute, IResourceFilter
{
    public void OnResourceExecuting(ResourceExecutingContext context)
    {
        context.Result = new ContentResult
        {
            Content = nameof(ShortCircuitingResourceFilterAttribute)
        };
    }

    public void OnResourceExecuted(ResourceExecutedContext context) { }
}

Nel codice seguente sia il filtro [ShortCircuitingResourceFilter] che il filtro [ResponseHeader] hanno come destinazione il metodo di azione Index. Filtro ShortCircuitingResourceFilterAttribute :

  • Viene eseguito per primo perché è un filtro risorsa e ResponseHeaderAttribute è un filtro azione.
  • Blocca il resto della pipeline.

Pertanto il filtro ResponseHeaderAttribute non viene mai eseguito per l'azione Index. Questo comportamento sarebbe lo stesso se entrambi i filtri venissero applicati a livello di metodo di azione, a condizione che il filtro ShortCircuitingResourceFilterAttribute venga eseguito prima. Viene ShortCircuitingResourceFilterAttribute eseguito per primo a causa del tipo di filtro:

[ResponseHeader("Filter-Header", "Filter Value")]
public class ShortCircuitingController : Controller
{
    [ShortCircuitingResourceFilter]
    public IActionResult Index() =>
        Content($"- {nameof(ShortCircuitingController)}.{nameof(Index)}");
}

Inserimento delle dipendenze

È possibile aggiungere filtri per tipo o per istanza. Se viene aggiunta un'istanza, tale istanza viene usata per ogni richiesta. Se viene aggiunto un tipo, il filtro è attivato dal tipo. Un filtro attivato dal tipo comporta:

  • La creazione di un'istanza per ogni richiesta.
  • Il popolamento di tutte le dipendenze del costruttore tramite inserimento delle dipendenze.

I filtri implementati come attributi e aggiunti direttamente alle classi controller o ai metodi di azione non possono avere dipendenze costruttore specificate dall'inserimento di dipendenze. Le dipendenze del costruttore non possono essere fornite dall'inserimento delle dipendenze dal costruttore perché gli attributi devono avere i parametri del costruttore specificati dove vengono applicati.

I filtri seguenti supportano le dipendenze del costruttore fornite dall'inserimento delle dipendenze:

I filtri precedenti possono essere applicati a un controller o a un'azione.

I logger sono disponibili tramite l'inserimento delle dipendenze. Evitare tuttavia di creare e usare filtri esclusivamente per scopi di registrazione. La funzionalità di registrazione del framework predefinita in genere fornisce il necessario per la registrazione. La registrazione aggiunta ai filtri:

  • Deve concentrarsi su problemi del dominio di business o comportamenti specifici del filtro.
  • Non deve registrare azioni o altri eventi del framework. I filtri predefiniti registrano già azioni ed eventi del framework.

ServiceFilterAttribute

I tipi di implementazione del filtro di servizi sono registrati in Program.cs. ServiceFilterAttribute recupera un'istanza del filtro dall'inserimento di dipendenze.

Il codice seguente illustra la classe , che usa l'inserimento LoggingResponseHeaderFilterService delle dipendenze:

public class LoggingResponseHeaderFilterService : IResultFilter
{
    private readonly ILogger _logger;

    public LoggingResponseHeaderFilterService(
            ILogger<LoggingResponseHeaderFilterService> logger) =>
        _logger = logger;

    public void OnResultExecuting(ResultExecutingContext context)
    {
        _logger.LogInformation(
            $"- {nameof(LoggingResponseHeaderFilterService)}.{nameof(OnResultExecuting)}");

        context.HttpContext.Response.Headers.Add(
            nameof(OnResultExecuting), nameof(LoggingResponseHeaderFilterService));
    }

    public void OnResultExecuted(ResultExecutedContext context)
    {
        _logger.LogInformation(
            $"- {nameof(LoggingResponseHeaderFilterService)}.{nameof(OnResultExecuted)}");
    }
}

Nel codice seguente l'oggetto LoggingResponseHeaderFilterService viene aggiunto al contenitore di inserimento delle dipendenze:

builder.Services.AddScoped<LoggingResponseHeaderFilterService>();

Nel codice seguente l'attributo ServiceFilter recupera un'istanza del filtro LoggingResponseHeaderFilterService dall'inserimento delle dipendenze:

[ServiceFilter<LoggingResponseHeaderFilterService>]
public IActionResult WithServiceFilter() =>
    Content($"- {nameof(FilterDependenciesController)}.{nameof(WithServiceFilter)}");

Quando si usa ServiceFilterAttribute, impostando ServiceFilterAttribute.IsReusable:

  • Indica che l'istanza del filtro può essere riutilizzata all'esterno dell'ambito della richiesta in cui è stata creata. Il runtime di ASP.NET Core non garantisce:
    • Che venga creata una singola istanza del filtro.
    • Che il filtro non verrà richiesto di nuovo dal contenitore di inserimento delle dipendenze in un momento successivo.
  • Non deve essere usato con un filtro che dipende dai servizi con una durata diversa da singleton.

ServiceFilterAttribute implementa IFilterFactory. IFilterFactory espone il metodo CreateInstance per creare un'istanza IFilterMetadata. CreateInstance carica il tipo specificato dall'inserimento delle dipendenze.

TypeFilterAttribute

TypeFilterAttribute è simile a ServiceFilterAttribute, ma il relativo tipo non viene risolto direttamente dal contenitore dell'inserimento di dipendenze. Viene creata un'istanza del tipo tramite Microsoft.Extensions.DependencyInjection.ObjectFactory.

Poiché i tipi TypeFilterAttribute non vengono risolti direttamente dal contenitore di inserimento delle dipendenze:

  • Non è necessario che i tipi a cui viene fatto riferimento tramite TypeFilterAttribute siano registrati nel contenitore di inserimento delle dipendenze. Le loro dipendenze vengono soddisfatte dal contenitore di inserimento delle dipendenze.
  • In via facoltativa, TypeFilterAttribute può anche accettare gli argomenti del costruttore per il tipo.

Quando si usa TypeFilterAttribute, impostando TypeFilterAttribute.IsReusable:

  • Indica che l'istanza del filtro può essere riutilizzata all'esterno dell'ambito della richiesta in cui è stata creata. Il runtime di ASP.NET Core non fornisce alcuna garanzia che venga creata una singola istanza del filtro.

  • Non si deve usare con un filtro che dipende da servizi con una durata diversa da singleton.

L'esempio seguente illustra come passare argomenti a un tipo usando TypeFilterAttribute:

[TypeFilter(typeof(LoggingResponseHeaderFilter),
    Arguments = new object[] { "Filter-Header", "Filter Value" })]
public IActionResult WithTypeFilter() =>
    Content($"- {nameof(FilterDependenciesController)}.{nameof(WithTypeFilter)}");

Filtri autorizzazione

I filtri di autorizzazione:

  • Sono i primi filtri a essere eseguiti nella pipeline di filtro.
  • Controllano l'accesso ai metodi di azione.
  • Dispongono di un metodo precedente, ma non di un metodo successivo.

I filtri di autorizzazione personalizzati richiedono un framework di autorizzazione personalizzato. È preferibile configurare criteri di autorizzazione o scrivere criteri di autorizzazione personalizzati piuttosto che scrivere un filtro personalizzato. Il filtro di autorizzazione predefinito:

  • Chiama il sistema di autorizzazione.
  • Non autorizza le richieste.

Non generare eccezioni nei filtri di autorizzazione:

  • L'eccezione non verrà gestita.
  • I filtri di eccezione non gestiranno l'eccezione.

È consigliabile emettere una richiesta di verifica in caso di eccezione in un filtro di autorizzazione.

Altre informazioni sull'autorizzazione.

Filtri risorse

I filtri di risorse:

I filtri di risorse sono utili per causare il corto circuito della maggior parte della pipeline. Un filtro di memorizzazione nella cache può, ad esempio, evitare il resto della pipeline in caso di riscontro nella cache.

Esempi di filtri di risorse:

Filtri azione

I filtri azione non si applicano alle Razor pagine. Razor Pages supporta IPageFilter e IAsyncPageFilter. Per altre informazioni, vedere Metodi di filtro per Razor Pagine.

I filtri di azione:

Il codice seguente mostra un filtro di azione di esempio:

public class SampleActionFilter : IActionFilter
{
    public void OnActionExecuting(ActionExecutingContext context)
    {
        // Do something before the action executes.
    }

    public void OnActionExecuted(ActionExecutedContext context)
    {
        // Do something after the action executes.
    }
}

La classe ActionExecutingContext specifica le proprietà seguenti:

  • ActionArguments : consente di leggere gli input in un metodo di azione.
  • Controller: consente di modificare l'istanza del controller.
  • Result: l'impostazione di Result causa il corto circuito del metodo di azione e dei filtri di azione successivi.

La generazione di un'eccezione in un metodo di azione:

  • Impedisce l'esecuzione dei filtri successivi.
  • A differenza dell'impostazione di Result, è considerata un errore anziché un risultato positivo.

La classe ActionExecutedContext specifica Controller e Result oltre alle proprietà seguenti:

  • Canceled: true se un altro file ha causato il corto circuito dell'azione.
  • Exception: non Null se l'azione o un filtro di azione eseguito in precedenza ha generato un'eccezione. Impostazione di questa proprietà su null:
    • L'eccezione viene gestita in modo efficace.
    • L'oggetto Result viene eseguito come se fosse stato restituito dal metodo di azione.

Per un oggetto IAsyncActionFilter, una chiamata a ActionExecutionDelegate:

  • Esegue qualsiasi filtro azione successivo e il metodo di azione.
  • Restituisce ActionExecutedContext.

Per causare il corto circuito, assegnare Microsoft.AspNetCore.Mvc.Filters.ActionExecutingContext.Result a un'istanza del risultato e non chiamare next (ActionExecutionDelegate).

Il framework fornisce un oggetto ActionFilterAttribute astratto che è possibile impostare come sottoclasse.

Il filtro di azione OnActionExecuting può essere usato per:

  • Convalidare lo stato del modello.
  • Restituire un errore se lo stato non è valido.
public class ValidateModelAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext context)
    {
        if (!context.ModelState.IsValid)
        {
            context.Result = new BadRequestObjectResult(context.ModelState);
        }
    }
}

Nota

I controller annotati con l'attributo convalidano automaticamente lo stato del [ApiController] modello e restituiscono una risposta 400. Per altre informazioni, vedere Risposte HTTP 400 automatiche.

Il metodo OnActionExecuted viene eseguito dopo il metodo di azione:

  • È possibile visualizzare e modificare i risultati dell'azione tramite la proprietà Result.
  • L'impostazione di Canceled è true se un altro filtro ha causato il corto circuito dell'esecuzione dell'azione.
  • L'impostazione di Exception è un valore non Null se l'azione o un filtro di azione successivo ha generato un'eccezione. Impostazione su Exception null:
    • Gestisce un'eccezione in modo efficace.
    • L'oggetto ActionExecutedContext.Result viene eseguito come se fosse stato restituito normalmente dal metodo di azione.

Filtri eccezioni

Filtri eccezioni:

Il filtro eccezioni di esempio seguente mostra i dettagli sulle eccezioni che si verificano quando l'app è in fase di sviluppo:

public class SampleExceptionFilter : IExceptionFilter
{
    private readonly IHostEnvironment _hostEnvironment;

    public SampleExceptionFilter(IHostEnvironment hostEnvironment) =>
        _hostEnvironment = hostEnvironment;

    public void OnException(ExceptionContext context)
    {
        if (!_hostEnvironment.IsDevelopment())
        {
            // Don't display exception details unless running in Development.
            return;
        }

        context.Result = new ContentResult
        {
            Content = context.Exception.ToString()
        };
    }
}

Il codice seguente verifica il filtro delle eccezioni:

[TypeFilter<SampleExceptionFilter>]
public class ExceptionController : Controller
{
    public IActionResult Index() =>
        Content($"- {nameof(ExceptionController)}.{nameof(Index)}");
}

Filtri eccezioni:

  • Non dispone di eventi precedenti o successivi.
  • Implementano OnException o OnExceptionAsync.
  • Gestire le eccezioni non gestite che si verificano nella creazione di pagine o controller, nell'associazione Razor di modelli, nei filtri di azione o nei metodi di azione.
  • Non rilevano le eccezioni che si verificano nei filtri di risorse, nei filtri dei risultati o nell'esecuzione dei risultati MVC.

Per gestire un'eccezione, impostare la ExceptionHandled proprietà su true o assegnare la Result proprietà . In questo modo si arresta la propagazione dell'eccezione. Un filtro di eccezione non può modificare un'eccezione in una "operazione riuscita". Questa operazione può essere eseguita solo tramite un filtro di azione.

Filtri eccezioni:

  • Sono ideali per intercettare le eccezioni che si verificano nelle azioni.
  • Non sono flessibili quanto il middleware di gestione degli errori.

Scegliere il middleware per la gestione delle eccezioni. Usare i filtri di eccezione solo quando la gestione degli errori varia in base al metodo di azione chiamato. Un'app può ad esempio avere metodi di azione sia per gli endpoint API che per le visualizzazioni/HTML. Gli endpoint API potrebbero restituire informazioni sull'errore come JSON, mentre le azioni basate sulla visualizzazione potrebbero restituire una pagina di errore come HTML.

Filtri risultato

I filtri dei risultati:

IResultFilter e IAsyncResultFilter

Il codice seguente mostra un filtro dei risultati di esempio:

public class SampleResultFilter : IResultFilter
{
    public void OnResultExecuting(ResultExecutingContext context)
    {
        // Do something before the result executes.
    }

    public void OnResultExecuted(ResultExecutedContext context)
    {
        // Do something after the result executes.
    }
}

Il tipo di risultato eseguito dipende dall'azione. Un'azione che restituisce una visualizzazione include tutta l'elaborazione razor come parte dell'esecuzione ViewResult . Un metodo API può eseguire la serializzazione in quanto parte dell'esecuzione del risultato. Altre informazioni sui risultati delle azioni.

I filtri dei risultati vengono eseguiti solo quando un'azione o un filtro azione produce un risultato dell'azione. I filtri dei risultati non vengono eseguiti quando:

  • Un filtro di autorizzazione o un filtro di risorse cortocircuita la pipeline.
  • Un filtro di eccezione non gestisca un'eccezione producendo un risultato dell'azione.

Il metodo Microsoft.AspNetCore.Mvc.Filters.IResultFilter.OnResultExecuting può causare il corto circuito dell'esecuzione del risultato dell'azione e dei filtri dei risultati successivi se si imposta Microsoft.AspNetCore.Mvc.Filters.ResultExecutingContext.Cancel su true. In caso di corto circuito, scrivere nell'oggetto risposta per evitare di generare una risposta vuota. Generazione di un'eccezione in IResultFilter.OnResultExecuting:

  • Impedisce l'esecuzione del risultato dell'azione e dei filtri successivi.
  • Viene considerato come un errore anziché come risultato positivo.

Quando il Microsoft.AspNetCore.Mvc.Filters.IResultFilter.OnResultExecuted metodo viene eseguito, è probabile che la risposta sia già stata inviata al client. Se la risposta è già stata inviata al client, non può essere modificata.

L'impostazione di ResultExecutedContext.Canceled è true se un altro filtro ha causato il corto circuito dell'esecuzione del risultato dell'azione.

L'impostazione di ResultExecutedContext.Exception è un valore non Null se il risultato dell'azione o un filtro dei risultati successivo ha generato un'eccezione. L'impostazione su Exception Null gestisce in modo efficace un'eccezione e impedisce che l'eccezione venga generata nuovamente più avanti nella pipeline. Non c'è alcun modo affidabile per scrivere i dati in una risposta quando si gestisce un'eccezione in un filtro dei risultati. Se le intestazioni sono state scaricate nel client quando il risultato di un'azione genera un'eccezione, non c'è alcun meccanismo affidabile per inviare un codice di errore.

Per un oggetto IAsyncResultFilter, una chiamata a await next in ResultExecutionDelegate esegue tutti i filtri dei risultati successivi e il risultato dell'azione. Per corto circuito, impostare su ResultExecutingContext.Canceltrue e non chiamare :ResultExecutionDelegate

public class SampleAsyncResultFilter : IAsyncResultFilter
{
    public async Task OnResultExecutionAsync(
        ResultExecutingContext context, ResultExecutionDelegate next)
    {
        if (context.Result is not EmptyResult)
        {
            await next();
        }
        else
        {
            context.Cancel = true;
        }
    }
}

Il framework fornisce un oggetto ResultFilterAttribute astratto che è possibile impostare come sottoclasse. La classe ResponseHeaderAttribute illustrata in precedenza è un esempio di attributo di filtro dei risultati.

IAlwaysRunResultFilter e IAsyncAlwaysRunResultFilter

Le interfacce IAlwaysRunResultFilter e IAsyncAlwaysRunResultFilter dichiarano un'implementazione di IResultFilter eseguita per tutti i risultati dell'azione. Sono inclusi i risultati dell'azione prodotti da:

  • Filtri di autorizzazione e filtri delle risorse che causano un corto circuito.
  • Filtri eccezioni.

Ad esempio, il filtro seguente viene eseguito sempre e imposta un risultato dell'azione (ObjectResult) con un codice di stato 422 Entità non elaborabile quando la negoziazione del contenuto ha esito negativo:

public class UnprocessableResultFilter : IAlwaysRunResultFilter
{
    public void OnResultExecuting(ResultExecutingContext context)
    {
        if (context.Result is StatusCodeResult statusCodeResult
            && statusCodeResult.StatusCode == StatusCodes.Status415UnsupportedMediaType)
        {
            context.Result = new ObjectResult("Unprocessable")
            {
                StatusCode = StatusCodes.Status422UnprocessableEntity
            };
        }
    }

    public void OnResultExecuted(ResultExecutedContext context) { }
}

IFilterFactory

IFilterFactory implementa IFilterMetadata. Pertanto, un'istanza di IFilterFactory può essere usata come un'istanza di IFilterMetadata in un punto qualsiasi della pipeline filtro. Quando il runtime si prepara per richiamare il filtro, cerca di eseguirne il cast a un oggetto IFilterFactory. Se l'esecuzione del cast ha esito positivo, viene chiamato il metodo CreateInstance per creare l'istanza di IFilterMetadata richiamata. In questo modo viene specificata una struttura flessibile, poiché non è necessario impostare in modo esplicito la pipeline filtro all'avvio dell'app.

IFilterFactory.IsReusable:

  • Suggerimento della factory che l'istanza del filtro creata dalla factory può essere riutilizzata all'esterno dell'ambito della richiesta in cui è stata creata.
  • Non deve essere usato con un filtro che dipende dai servizi con una durata diversa da singleton.

Il runtime di ASP.NET Core non garantisce:

  • Che venga creata una singola istanza del filtro.
  • Che il filtro non verrà richiesto di nuovo dal contenitore di inserimento delle dipendenze in un momento successivo.

Avviso

Configurare per restituire la restituzione IFilterFactory.IsReusabletrue solo se l'origine dei filtri non è ambigua, i filtri sono senza stato e i filtri sono sicuri da usare tra più richieste HTTP. Ad esempio, non restituire filtri dall'inserimento di dipendenze registrati come ambito o temporanei se IFilterFactory.IsReusable restituisce true.

Un altro approccio alla creazione di filtri consiste nell'implementare IFilterFactory usando implementazioni dell'attributo personalizzate:

public class ResponseHeaderFilterFactory : Attribute, IFilterFactory
{
    public bool IsReusable => false;

    public IFilterMetadata CreateInstance(IServiceProvider serviceProvider) =>
        new InternalResponseHeaderFilter();

    private class InternalResponseHeaderFilter : IActionFilter
    {
        public void OnActionExecuting(ActionExecutingContext context) =>
            context.HttpContext.Response.Headers.Add(
                nameof(OnActionExecuting), nameof(InternalResponseHeaderFilter));

        public void OnActionExecuted(ActionExecutedContext context) { }
    }

Il filtro viene applicato nel codice seguente:

[ResponseHeaderFilterFactory]
public IActionResult Index() =>
    Content($"- {nameof(FilterFactoryController)}.{nameof(Index)}");

Implementazione di IFilterFactory in un attributo

I filtri che implementano IFilterFactory sono utili per i filtri che:

  • Non richiedono il passaggio di parametri.
  • Hanno dipendenze del costruttore che devono essere soddisfatte dall'inserimento delle dipendenze.

TypeFilterAttribute implementa IFilterFactory. IFilterFactory espone il metodo CreateInstance per creare un'istanza IFilterMetadata. CreateInstance carica il tipo specificato dal contenitore di inserimento delle dipendenze.

public class SampleActionTypeFilterAttribute : TypeFilterAttribute
{
    public SampleActionTypeFilterAttribute()
         : base(typeof(InternalSampleActionFilter)) { }

    private class InternalSampleActionFilter : IActionFilter
    {
        private readonly ILogger<InternalSampleActionFilter> _logger;

        public InternalSampleActionFilter(ILogger<InternalSampleActionFilter> logger) =>
            _logger = logger;

        public void OnActionExecuting(ActionExecutingContext context)
        {
            _logger.LogInformation(
                $"- {nameof(InternalSampleActionFilter)}.{nameof(OnActionExecuting)}");
        }

        public void OnActionExecuted(ActionExecutedContext context)
        {
            _logger.LogInformation(
                $"- {nameof(InternalSampleActionFilter)}.{nameof(OnActionExecuted)}");
        }
    }
}

Il codice seguente illustra tre approcci per l'applicazione del filtro:

[SampleActionTypeFilter]
public IActionResult WithDirectAttribute() =>
    Content($"- {nameof(FilterFactoryController)}.{nameof(WithDirectAttribute)}");

[TypeFilter<SampleActionTypeFilterAttribute>]
public IActionResult WithTypeFilterAttribute() =>
    Content($"- {nameof(FilterFactoryController)}.{nameof(WithTypeFilterAttribute)}");

[ServiceFilter<SampleActionTypeFilterAttribute>]
public IActionResult WithServiceFilterAttribute() =>
    Content($"- {nameof(FilterFactoryController)}.{nameof(WithServiceFilterAttribute)}");

Nel codice precedente è preferibile il primo approccio all'applicazione del filtro.

Usare il middleware nella pipeline di filtro

I filtri risorse funzionano come middleware in quanto racchiudono l'esecuzione di tutto ciò che viene dopo nella pipeline. Tuttavia, i filtri differiscono dal middleware in quanto fanno parte del runtime, il che significa che hanno accesso al contesto e ai costrutti.

Per usare il middleware come filtro, creare un tipo con un metodo Configure che specifica il middleware da inserire nella pipeline di filtro. Nell'esempio seguente viene usato il middleware per impostare un'intestazione di risposta:

public class FilterMiddlewarePipeline
{
    public void Configure(IApplicationBuilder app)
    {
        app.Use(async (context, next) =>
        {
            context.Response.Headers.Add("Pipeline", "Middleware");

            await next();
        });
    }
}

Usare MiddlewareFilterAttribute per eseguire il middleware:

[MiddlewareFilter<FilterMiddlewarePipeline>]
public class FilterMiddlewareController : Controller
{
    public IActionResult Index() =>
        Content($"- {nameof(FilterMiddlewareController)}.{nameof(Index)}");
}

I filtri middleware vengono eseguiti nella stessa fase della pipeline filtro come filtri risorse, prima dell'associazione di modelli e dopo il resto della pipeline.

Thread safety

Quando si passa un'istanza di un filtro in Add, anziché il relativo Type, il filtro è un singleton e non è thread-safe.

Risorse aggiuntive

Di Kirk Larkin, Rick Anderson, Tom Dykstra e Steve Smith

I filtri in ASP.NET Core consentono l'esecuzione del codice prima o dopo fasi specifiche nella pipeline di elaborazione delle richieste.

I filtri predefiniti gestiscono attività, ad esempio:

  • Autorizzazione, che impedisce l'accesso alle risorse per cui un utente non è autorizzato.
  • Memorizzazione nella cache delle risposte, corto circuito della pipeline di richiesta per restituire una risposta memorizzata nella cache.

I filtri personalizzati possono essere creati per gestire problemi relativi a più settori. Esempi di problematiche trasversali includono la gestione degli errori, la memorizzazione nella cache, la configurazione, l'autorizzazione e la registrazione. I filtri evitano la duplicazione del codice. Ad esempio, un filtro eccezioni per la gestione degli errori potrebbe consolidare la gestione degli errori.

Questo documento si applica a Razor Pagine, controller API e controller con visualizzazioni. I filtri non funzionano direttamente con i Razor componenti. Un filtro può influire indirettamente su un componente quando:

  • Il componente è incorporato in una pagina o in una visualizzazione.
  • La pagina o il controller e la visualizzazione usano il filtro.

Funzionamento dei filtri

I filtri vengono eseguiti all'interno della pipeline di chiamata di azioni ASP.NET Core, talvolta definita pipeline di filtro. La pipeline di filtro viene eseguita dopo ASP.NET Core seleziona l'azione da eseguire:

The request is processed through Other Middleware, Routing Middleware, Action Selection, and the Action Invocation Pipeline. The request processing continues back through Action Selection, Routing Middleware, and various Other Middleware before becoming a response sent to the client.

Tipi di filtri

Ogni tipo di filtro viene eseguito in una fase diversa della pipeline di filtro:

  • Filtri autorizzazione:

    • Eseguire prima di tutto.
    • Determinare se l'utente è autorizzato per la richiesta.
    • Corto circuito della pipeline se la richiesta non è autorizzata.
  • Filtri di risorse:

    • Vengono eseguiti dopo l'autorizzazione.
    • OnResourceExecuting esegue il codice prima del resto della pipeline del filtro. Ad esempio, OnResourceExecuting esegue il codice prima dell'associazione di modelli.
    • OnResourceExecuted esegue il codice dopo il completamento del resto della pipeline.
  • Filtri azione:

    • Eseguire immediatamente prima e dopo la chiamata di un metodo di azione.
    • Può modificare gli argomenti passati in un'azione.
    • Può modificare il risultato restituito dall'azione.
    • Non sono supportati in Razor Pages.
  • Filtri endpoint:

    • Eseguire immediatamente prima e dopo la chiamata di un metodo di azione.
    • Può modificare gli argomenti passati in un'azione.
    • Può modificare il risultato restituito dall'azione.
    • Non sono supportati in Razor Pages.
    • Può essere richiamato su entrambe le azioni e sugli endpoint basati sul gestore di route.
  • I filtri eccezioni applicano criteri globali a eccezioni non gestite che si verificano prima che il corpo della risposta sia stato scritto in.

  • Filtri dei risultati:

    • Eseguire immediatamente prima e dopo l'esecuzione dei risultati dell'azione.
    • Eseguire solo quando il metodo di azione viene eseguito correttamente.
    • Sono utili per la logica che deve racchiudere l'esecuzione della visualizzazione o del formattatore.

Il diagramma seguente mostra come interagiscono i tipi di filtro nella pipeline di filtro:

The request is processed through Authorization Filters, Resource Filters, Model Binding, Action Filters, Action Execution and Action Result Conversion, Exception Filters, Result Filters, and Result Execution. On the way out, the request is only processed by Result Filters and Resource Filters before becoming a response sent to the client.

Razor Le pagine supportano Razor anche i filtri pagina, che vengono eseguiti prima e dopo un Razor gestore di pagine.

Implementazione

I filtri supportano sia implementazioni sincrone che asincrone tramite definizioni di interfaccia diverse.

I filtri sincroni vengono eseguiti prima e dopo la fase della pipeline. La chiamata di OnActionExecuting, ad esempio, avviene prima della chiamata del metodo di azione. OnActionExecuted viene chiamato dopo che il metodo di azione restituisce:

public class SampleActionFilter : IActionFilter
{
    public void OnActionExecuting(ActionExecutingContext context)
    {
        // Do something before the action executes.
    }

    public void OnActionExecuted(ActionExecutedContext context)
    {
        // Do something after the action executes.
    }
}

I filtri asincroni definiscono un On-Stage-ExecutionAsync metodo. Ad esempio, OnActionExecutionAsync:

public class SampleAsyncActionFilter : IAsyncActionFilter
{
    public async Task OnActionExecutionAsync(
        ActionExecutingContext context, ActionExecutionDelegate next)
    {
        // Do something before the action executes.
        await next();
        // Do something after the action executes.
    }
}

Nel codice precedente l'oggetto SampleAsyncActionFilter ha un ActionExecutionDelegateoggetto , nextche esegue il metodo di azione.

Fasi di filtro multiple

È possibile implementare interfacce per più fasi di filtro in una singola classe. Ad esempio, la ActionFilterAttribute classe implementa:

Implementare la versione sincrona oppure la versione asincrona di un'interfaccia di filtro, non entrambe. Il runtime controlla per prima cosa se il filtro implementa l'interfaccia asincrona e, in tal caso, la chiama. In caso contrario, chiama i metodi dell'interfaccia sincrona. Se in una classe sono implementate entrambe le interfacce, sincrona e asincrona, viene chiamato solo il metodo asincrono. Quando si usano classi astratte come ActionFilterAttribute, eseguire l'override solo dei metodi sincroni o dei metodi asincroni per ogni tipo di filtro.

Attributi filtro predefiniti

ASP.NET Core include filtri predefiniti basati su attributi che è possibile impostare come sottoclasse e personalizzare. Il filtro dei risultati seguente, ad esempio, aggiunge un'intestazione alla risposta:

public class ResponseHeaderAttribute : ActionFilterAttribute
{
    private readonly string _name;
    private readonly string _value;

    public ResponseHeaderAttribute(string name, string value) =>
        (_name, _value) = (name, value);

    public override void OnResultExecuting(ResultExecutingContext context)
    {
        context.HttpContext.Response.Headers.Add(_name, _value);

        base.OnResultExecuting(context);
    }
}

Gli attributi consentono ai filtri di accettare argomenti, come illustrato nell'esempio precedente. Applicare ResponseHeaderAttribute a un controller o a un metodo di azione e specificare il nome e il valore dell'intestazione HTTP:

[ResponseHeader("Filter-Header", "Filter Value")]
public class ResponseHeaderController : ControllerBase
{
    public IActionResult Index() =>
        Content("Examine the response headers using the F12 developer tools.");

    // ...

Usare uno strumento come gli strumenti di sviluppo del browser per esaminare le intestazioni. In Intestazionifilter-header: Filter Value di risposta viene visualizzato.

Il codice seguente si applica ResponseHeaderAttribute sia a un controller che a un'azione:

[ResponseHeader("Filter-Header", "Filter Value")]
public class ResponseHeaderController : ControllerBase
{
    public IActionResult Index() =>
        Content("Examine the response headers using the F12 developer tools.");

    // ...

    [ResponseHeader("Another-Filter-Header", "Another Filter Value")]
    public IActionResult Multiple() =>
        Content("Examine the response headers using the F12 developer tools.");
}

Le risposte dell'azione Multiple includono le intestazioni seguenti:

  • filter-header: Filter Value
  • another-filter-header: Another Filter Value

Molte delle interfacce del filtro presentano attributi corrispondenti che possono essere usati come classi di base per implementazioni personalizzate.

Attributi dei filtri:

I filtri non possono essere applicati ai Razor metodi del gestore Page. Possono essere applicati al Razor modello Page o a livello globale.

Ambiti dei filtri e ordine di esecuzione

È possibile aggiungere un filtro alla pipeline in uno dei tre ambiti:

  • Uso di un attributo in un controller o Razor in una pagina.
  • Uso di un attributo in un'azione del controller. Gli attributi di filtro non possono essere applicati ai Razor metodi del gestore Pages.
  • A livello globale per tutti i controller, le azioni e Razor le pagine, come illustrato nel codice seguente:
    var builder = WebApplication.CreateBuilder(args);
    
    // Add services to the container.
    builder.Services.AddControllersWithViews(options =>
    {
        options.Filters.Add<GlobalSampleActionFilter>();
    });
    

Ordine di esecuzione predefinito

Quando sono presenti più filtri per una particolare fase della pipeline, l'ambito determina l'ordine di esecuzione predefinito dei filtri. I filtri globali racchiudono i filtri di classe, che a loro volta racchiudono i filtri dei metodi.

Come risultato dell'annidamento dei filtri, il codice after dei filtri viene eseguito in ordine inverso rispetto al codice before. Sequenza di filtro:

  • Codice before dei filtri globali.
    • Codice before dei filtri del controller.
      • Codice before dei filtri del metodo di azione.
      • Codice after dei filtri del metodo di azione.
    • Codice after dei filtri del controller.
  • Codice after dei filtri globali.

Nell'esempio seguente viene illustrato l'ordine in cui vengono eseguiti i metodi di filtro per i filtri di azione sincroni:

Sequence Ambito del filtro Metodo del filtro
1 Generale OnActionExecuting
2 Controller OnActionExecuting
3 Azione OnActionExecuting
4 Azione OnActionExecuted
5 Controller OnActionExecuted
6 Generale OnActionExecuted

Filtri a livello di controller

Ogni controller che eredita da Controller include i OnActionExecutingmetodi , OnActionExecutionAsynce OnActionExecuted . Questi metodi eseguono il wrapping dei filtri eseguiti per una determinata azione:

  • OnActionExecuting viene eseguito prima di uno dei filtri dell'azione.
  • OnActionExecuted viene eseguito dopo tutti i filtri dell'azione.
  • OnActionExecutionAsync viene eseguito prima di uno dei filtri dell'azione. Codice dopo l'esecuzione di una chiamata a next dopo i filtri dell'azione.

La classe seguente ControllerFiltersController :

  • Applica l'oggetto SampleActionFilterAttribute ([SampleActionFilter]) al controller.
  • Esegue l'override di OnActionExecuting e OnActionExecuted.
[SampleActionFilter]
public class ControllerFiltersController : Controller
{
    public override void OnActionExecuting(ActionExecutingContext context)
    {
        Console.WriteLine(
            $"- {nameof(ControllerFiltersController)}.{nameof(OnActionExecuting)}");

        base.OnActionExecuting(context);
    }

    public override void OnActionExecuted(ActionExecutedContext context)
    {
        Console.WriteLine(
            $"- {nameof(ControllerFiltersController)}.{nameof(OnActionExecuted)}");

        base.OnActionExecuted(context);
    }

    public IActionResult Index()
    {
        Console.WriteLine(
            $"- {nameof(ControllerFiltersController)}.{nameof(Index)}");

        return Content("Check the Console.");
    }
}

Passando a https://localhost:<port>/ControllerFilters, viene eseguito il codice seguente:

  • ControllerFiltersController.OnActionExecuting
    • GlobalSampleActionFilter.OnActionExecuting
      • SampleActionFilterAttribute.OnActionExecuting
        • ControllerFiltersController.Index
      • SampleActionFilterAttribute.OnActionExecuted
    • GlobalSampleActionFilter.OnActionExecuted
  • ControllerFiltersController.OnActionExecuted

I filtri a livello di controller impostano la proprietà Order su int.MinValue. Non è possibile impostare filtri a livello di controller per l'esecuzione dopo l'applicazione dei filtri ai metodi. L'ordine è illustrato nella sezione successiva.

Per Razor le pagine, vedere Implementare Razor filtri di pagina eseguendo l'override dei metodi di filtro.

Eseguire l'override dell'ordine predefinito

È possibile eseguire l'override della sequenza di esecuzione predefinita implementando IOrderedFilter. IOrderedFilter espone la proprietà Order, che ha la precedenza sull'ambito per determinare l'ordine di esecuzione. Un filtro con un valore di Order inferiore:

  • Esegue il codice before prima di quello di un filtro con un valore di Order più alto.
  • Esegue il codice after dopo quello di un filtro con un valore di Order più alto.

Nell'esempio di filtri a livello di controller ha GlobalSampleActionFilter un ambito globale in modo che venga eseguito prima SampleActionFilterAttributedi , con ambito controller. Per eseguire SampleActionFilterAttribute prima di tutto, impostarne l'ordine su int.MinValue:

[SampleActionFilter(Order = int.MinValue)]
public class ControllerFiltersController : Controller
{
    // ...
}

Per fare in modo che il filtro GlobalSampleActionFilter globale sia eseguito per primo, impostarlo Order su int.MinValue:

builder.Services.AddControllersWithViews(options =>
{
    options.Filters.Add<GlobalSampleActionFilter>(int.MinValue);
});

Annullamento e corto circuito

È possibile causare il corto circuito della pipeline di filtro impostando la proprietà Result sul parametro ResourceExecutingContext fornito al metodo di filtro. Ad esempio, il filtro risorsa seguente impedisce l'esecuzione del resto della pipeline:

public class ShortCircuitingResourceFilterAttribute : Attribute, IResourceFilter
{
    public void OnResourceExecuting(ResourceExecutingContext context)
    {
        context.Result = new ContentResult
        {
            Content = nameof(ShortCircuitingResourceFilterAttribute)
        };
    }

    public void OnResourceExecuted(ResourceExecutedContext context) { }
}

Nel codice seguente sia il filtro [ShortCircuitingResourceFilter] che il filtro [ResponseHeader] hanno come destinazione il metodo di azione Index. Filtro ShortCircuitingResourceFilterAttribute :

  • Viene eseguito per primo perché è un filtro risorsa e ResponseHeaderAttribute è un filtro azione.
  • Blocca il resto della pipeline.

Pertanto il filtro ResponseHeaderAttribute non viene mai eseguito per l'azione Index. Questo comportamento sarebbe lo stesso se entrambi i filtri venissero applicati a livello di metodo di azione, a condizione che il filtro ShortCircuitingResourceFilterAttribute venga eseguito prima. Viene ShortCircuitingResourceFilterAttribute eseguito per primo a causa del tipo di filtro:

[ResponseHeader("Filter-Header", "Filter Value")]
public class ShortCircuitingController : Controller
{
    [ShortCircuitingResourceFilter]
    public IActionResult Index() =>
        Content($"- {nameof(ShortCircuitingController)}.{nameof(Index)}");
}

Inserimento delle dipendenze

È possibile aggiungere filtri per tipo o per istanza. Se viene aggiunta un'istanza, tale istanza viene usata per ogni richiesta. Se viene aggiunto un tipo, il filtro è attivato dal tipo. Un filtro attivato dal tipo comporta:

  • La creazione di un'istanza per ogni richiesta.
  • Il popolamento di tutte le dipendenze del costruttore tramite inserimento delle dipendenze.

I filtri implementati come attributi e aggiunti direttamente alle classi controller o ai metodi di azione non possono avere dipendenze costruttore specificate dall'inserimento di dipendenze. Le dipendenze del costruttore non possono essere fornite dall'inserimento delle dipendenze dal costruttore perché gli attributi devono avere i parametri del costruttore specificati dove vengono applicati.

I filtri seguenti supportano le dipendenze del costruttore fornite dall'inserimento delle dipendenze:

I filtri precedenti possono essere applicati a un controller o a un'azione.

I logger sono disponibili tramite l'inserimento delle dipendenze. Evitare tuttavia di creare e usare filtri esclusivamente per scopi di registrazione. La funzionalità di registrazione del framework predefinita in genere fornisce il necessario per la registrazione. La registrazione aggiunta ai filtri:

  • Deve concentrarsi su problemi del dominio di business o comportamenti specifici del filtro.
  • Non deve registrare azioni o altri eventi del framework. I filtri predefiniti registrano già azioni ed eventi del framework.

ServiceFilterAttribute

I tipi di implementazione del filtro di servizi sono registrati in Program.cs. ServiceFilterAttribute recupera un'istanza del filtro dall'inserimento di dipendenze.

Il codice seguente illustra la classe , che usa l'inserimento LoggingResponseHeaderFilterService delle dipendenze:

public class LoggingResponseHeaderFilterService : IResultFilter
{
    private readonly ILogger _logger;

    public LoggingResponseHeaderFilterService(
            ILogger<LoggingResponseHeaderFilterService> logger) =>
        _logger = logger;

    public void OnResultExecuting(ResultExecutingContext context)
    {
        _logger.LogInformation(
            $"- {nameof(LoggingResponseHeaderFilterService)}.{nameof(OnResultExecuting)}");

        context.HttpContext.Response.Headers.Add(
            nameof(OnResultExecuting), nameof(LoggingResponseHeaderFilterService));
    }

    public void OnResultExecuted(ResultExecutedContext context)
    {
        _logger.LogInformation(
            $"- {nameof(LoggingResponseHeaderFilterService)}.{nameof(OnResultExecuted)}");
    }
}

Nel codice seguente l'oggetto LoggingResponseHeaderFilterService viene aggiunto al contenitore di inserimento delle dipendenze:

builder.Services.AddScoped<LoggingResponseHeaderFilterService>();

Nel codice seguente l'attributo ServiceFilter recupera un'istanza del filtro LoggingResponseHeaderFilterService dall'inserimento delle dipendenze:

[ServiceFilter(typeof(LoggingResponseHeaderFilterService))]
public IActionResult WithServiceFilter() =>
    Content($"- {nameof(FilterDependenciesController)}.{nameof(WithServiceFilter)}");

Quando si usa ServiceFilterAttribute, impostando ServiceFilterAttribute.IsReusable:

  • Indica che l'istanza del filtro può essere riutilizzata all'esterno dell'ambito della richiesta in cui è stata creata. Il runtime di ASP.NET Core non garantisce:
    • Che venga creata una singola istanza del filtro.
    • Che il filtro non verrà richiesto di nuovo dal contenitore di inserimento delle dipendenze in un momento successivo.
  • Non deve essere usato con un filtro che dipende dai servizi con una durata diversa da singleton.

ServiceFilterAttribute implementa IFilterFactory. IFilterFactory espone il metodo CreateInstance per creare un'istanza IFilterMetadata. CreateInstance carica il tipo specificato dall'inserimento delle dipendenze.

TypeFilterAttribute

TypeFilterAttribute è simile a ServiceFilterAttribute, ma il relativo tipo non viene risolto direttamente dal contenitore dell'inserimento di dipendenze. Viene creata un'istanza del tipo tramite Microsoft.Extensions.DependencyInjection.ObjectFactory.

Poiché i tipi TypeFilterAttribute non vengono risolti direttamente dal contenitore di inserimento delle dipendenze:

  • Non è necessario che i tipi a cui viene fatto riferimento tramite TypeFilterAttribute siano registrati nel contenitore di inserimento delle dipendenze. Le loro dipendenze vengono soddisfatte dal contenitore di inserimento delle dipendenze.
  • In via facoltativa, TypeFilterAttribute può anche accettare gli argomenti del costruttore per il tipo.

Quando si usa TypeFilterAttribute, impostando TypeFilterAttribute.IsReusable:

  • Indica che l'istanza del filtro può essere riutilizzata all'esterno dell'ambito della richiesta in cui è stata creata. Il runtime di ASP.NET Core non fornisce alcuna garanzia che venga creata una singola istanza del filtro.

  • Non si deve usare con un filtro che dipende da servizi con una durata diversa da singleton.

L'esempio seguente illustra come passare argomenti a un tipo usando TypeFilterAttribute:

[TypeFilter(typeof(LoggingResponseHeaderFilter),
    Arguments = new object[] { "Filter-Header", "Filter Value" })]
public IActionResult WithTypeFilter() =>
    Content($"- {nameof(FilterDependenciesController)}.{nameof(WithTypeFilter)}");

Filtri autorizzazione

I filtri di autorizzazione:

  • Sono i primi filtri a essere eseguiti nella pipeline di filtro.
  • Controllano l'accesso ai metodi di azione.
  • Dispongono di un metodo precedente, ma non di un metodo successivo.

I filtri di autorizzazione personalizzati richiedono un framework di autorizzazione personalizzato. È preferibile configurare criteri di autorizzazione o scrivere criteri di autorizzazione personalizzati piuttosto che scrivere un filtro personalizzato. Il filtro di autorizzazione predefinito:

  • Chiama il sistema di autorizzazione.
  • Non autorizza le richieste.

Non generare eccezioni nei filtri di autorizzazione:

  • L'eccezione non verrà gestita.
  • I filtri di eccezione non gestiranno l'eccezione.

È consigliabile emettere una richiesta di verifica in caso di eccezione in un filtro di autorizzazione.

Altre informazioni sull'autorizzazione.

Filtri risorse

I filtri di risorse:

I filtri di risorse sono utili per causare il corto circuito della maggior parte della pipeline. Un filtro di memorizzazione nella cache può, ad esempio, evitare il resto della pipeline in caso di riscontro nella cache.

Esempi di filtri di risorse:

Filtri azione

I filtri azione non si applicano alle Razor pagine. Razor Pages supporta IPageFilter e IAsyncPageFilter. Per altre informazioni, vedere Metodi di filtro per Razor Pagine.

I filtri di azione:

Il codice seguente mostra un filtro di azione di esempio:

public class SampleActionFilter : IActionFilter
{
    public void OnActionExecuting(ActionExecutingContext context)
    {
        // Do something before the action executes.
    }

    public void OnActionExecuted(ActionExecutedContext context)
    {
        // Do something after the action executes.
    }
}

La classe ActionExecutingContext specifica le proprietà seguenti:

  • ActionArguments : consente di leggere gli input in un metodo di azione.
  • Controller: consente di modificare l'istanza del controller.
  • Result: l'impostazione di Result causa il corto circuito del metodo di azione e dei filtri di azione successivi.

La generazione di un'eccezione in un metodo di azione:

  • Impedisce l'esecuzione dei filtri successivi.
  • A differenza dell'impostazione di Result, è considerata un errore anziché un risultato positivo.

La classe ActionExecutedContext specifica Controller e Result oltre alle proprietà seguenti:

  • Canceled: true se un altro file ha causato il corto circuito dell'azione.
  • Exception: non Null se l'azione o un filtro di azione eseguito in precedenza ha generato un'eccezione. Impostazione di questa proprietà su null:
    • L'eccezione viene gestita in modo efficace.
    • L'oggetto Result viene eseguito come se fosse stato restituito dal metodo di azione.

Per un oggetto IAsyncActionFilter, una chiamata a ActionExecutionDelegate:

  • Esegue qualsiasi filtro azione successivo e il metodo di azione.
  • Restituisce ActionExecutedContext.

Per causare il corto circuito, assegnare Microsoft.AspNetCore.Mvc.Filters.ActionExecutingContext.Result a un'istanza del risultato e non chiamare next (ActionExecutionDelegate).

Il framework fornisce un oggetto ActionFilterAttribute astratto che è possibile impostare come sottoclasse.

Il filtro di azione OnActionExecuting può essere usato per:

  • Convalidare lo stato del modello.
  • Restituire un errore se lo stato non è valido.
public class ValidateModelAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext context)
    {
        if (!context.ModelState.IsValid)
        {
            context.Result = new BadRequestObjectResult(context.ModelState);
        }
    }
}

Nota

I controller annotati con l'attributo convalidano automaticamente lo stato del [ApiController] modello e restituiscono una risposta 400. Per altre informazioni, vedere Risposte HTTP 400 automatiche.

Il metodo OnActionExecuted viene eseguito dopo il metodo di azione:

  • È possibile visualizzare e modificare i risultati dell'azione tramite la proprietà Result.
  • L'impostazione di Canceled è true se un altro filtro ha causato il corto circuito dell'esecuzione dell'azione.
  • L'impostazione di Exception è un valore non Null se l'azione o un filtro di azione successivo ha generato un'eccezione. Impostazione su Exception null:
    • Gestisce un'eccezione in modo efficace.
    • L'oggetto ActionExecutedContext.Result viene eseguito come se fosse stato restituito normalmente dal metodo di azione.

Filtri eccezioni

Filtri eccezioni:

Il filtro eccezioni di esempio seguente mostra i dettagli sulle eccezioni che si verificano quando l'app è in fase di sviluppo:

public class SampleExceptionFilter : IExceptionFilter
{
    private readonly IHostEnvironment _hostEnvironment;

    public SampleExceptionFilter(IHostEnvironment hostEnvironment) =>
        _hostEnvironment = hostEnvironment;

    public void OnException(ExceptionContext context)
    {
        if (!_hostEnvironment.IsDevelopment())
        {
            // Don't display exception details unless running in Development.
            return;
        }

        context.Result = new ContentResult
        {
            Content = context.Exception.ToString()
        };
    }
}

Il codice seguente verifica il filtro delle eccezioni:

[TypeFilter(typeof(SampleExceptionFilter))]
public class ExceptionController : Controller
{
    public IActionResult Index() =>
        Content($"- {nameof(ExceptionController)}.{nameof(Index)}");
}

Filtri eccezioni:

  • Non dispone di eventi precedenti o successivi.
  • Implementano OnException o OnExceptionAsync.
  • Gestire le eccezioni non gestite che si verificano nella creazione di pagine o controller, nell'associazione Razor di modelli, nei filtri di azione o nei metodi di azione.
  • Non rilevano le eccezioni che si verificano nei filtri di risorse, nei filtri dei risultati o nell'esecuzione dei risultati MVC.

Per gestire un'eccezione, impostare la ExceptionHandled proprietà su true o assegnare la Result proprietà . In questo modo si arresta la propagazione dell'eccezione. Un filtro di eccezione non può modificare un'eccezione in una "operazione riuscita". Questa operazione può essere eseguita solo tramite un filtro di azione.

Filtri eccezioni:

  • Sono ideali per intercettare le eccezioni che si verificano nelle azioni.
  • Non sono flessibili quanto il middleware di gestione degli errori.

Scegliere il middleware per la gestione delle eccezioni. Usare i filtri di eccezione solo quando la gestione degli errori varia in base al metodo di azione chiamato. Un'app può ad esempio avere metodi di azione sia per gli endpoint API che per le visualizzazioni/HTML. Gli endpoint API potrebbero restituire informazioni sull'errore come JSON, mentre le azioni basate sulla visualizzazione potrebbero restituire una pagina di errore come HTML.

Filtri risultato

I filtri dei risultati:

IResultFilter e IAsyncResultFilter

Il codice seguente mostra un filtro dei risultati di esempio:

public class SampleResultFilter : IResultFilter
{
    public void OnResultExecuting(ResultExecutingContext context)
    {
        // Do something before the result executes.
    }

    public void OnResultExecuted(ResultExecutedContext context)
    {
        // Do something after the result executes.
    }
}

Il tipo di risultato eseguito dipende dall'azione. Un'azione che restituisce una visualizzazione include tutta l'elaborazione razor come parte dell'esecuzione ViewResult . Un metodo API può eseguire la serializzazione in quanto parte dell'esecuzione del risultato. Altre informazioni sui risultati delle azioni.

I filtri dei risultati vengono eseguiti solo quando un'azione o un filtro azione produce un risultato dell'azione. I filtri dei risultati non vengono eseguiti quando:

  • Un filtro di autorizzazione o un filtro di risorse cortocircuita la pipeline.
  • Un filtro di eccezione non gestisca un'eccezione producendo un risultato dell'azione.

Il metodo Microsoft.AspNetCore.Mvc.Filters.IResultFilter.OnResultExecuting può causare il corto circuito dell'esecuzione del risultato dell'azione e dei filtri dei risultati successivi se si imposta Microsoft.AspNetCore.Mvc.Filters.ResultExecutingContext.Cancel su true. In caso di corto circuito, scrivere nell'oggetto risposta per evitare di generare una risposta vuota. Generazione di un'eccezione in IResultFilter.OnResultExecuting:

  • Impedisce l'esecuzione del risultato dell'azione e dei filtri successivi.
  • Viene considerato come un errore anziché come risultato positivo.

Quando il Microsoft.AspNetCore.Mvc.Filters.IResultFilter.OnResultExecuted metodo viene eseguito, è probabile che la risposta sia già stata inviata al client. Se la risposta è già stata inviata al client, non può essere modificata.

L'impostazione di ResultExecutedContext.Canceled è true se un altro filtro ha causato il corto circuito dell'esecuzione del risultato dell'azione.

L'impostazione di ResultExecutedContext.Exception è un valore non Null se il risultato dell'azione o un filtro dei risultati successivo ha generato un'eccezione. L'impostazione su Exception Null gestisce in modo efficace un'eccezione e impedisce che l'eccezione venga generata nuovamente più avanti nella pipeline. Non c'è alcun modo affidabile per scrivere i dati in una risposta quando si gestisce un'eccezione in un filtro dei risultati. Se le intestazioni sono state scaricate nel client quando il risultato di un'azione genera un'eccezione, non c'è alcun meccanismo affidabile per inviare un codice di errore.

Per un oggetto IAsyncResultFilter, una chiamata a await next in ResultExecutionDelegate esegue tutti i filtri dei risultati successivi e il risultato dell'azione. Per corto circuito, impostare su ResultExecutingContext.Canceltrue e non chiamare :ResultExecutionDelegate

public class SampleAsyncResultFilter : IAsyncResultFilter
{
    public async Task OnResultExecutionAsync(
        ResultExecutingContext context, ResultExecutionDelegate next)
    {
        if (context.Result is not EmptyResult)
        {
            await next();
        }
        else
        {
            context.Cancel = true;
        }
    }
}

Il framework fornisce un oggetto ResultFilterAttribute astratto che è possibile impostare come sottoclasse. La classe ResponseHeaderAttribute illustrata in precedenza è un esempio di attributo di filtro dei risultati.

IAlwaysRunResultFilter e IAsyncAlwaysRunResultFilter

Le interfacce IAlwaysRunResultFilter e IAsyncAlwaysRunResultFilter dichiarano un'implementazione di IResultFilter eseguita per tutti i risultati dell'azione. Sono inclusi i risultati dell'azione prodotti da:

  • Filtri di autorizzazione e filtri delle risorse che causano un corto circuito.
  • Filtri eccezioni.

Ad esempio, il filtro seguente viene eseguito sempre e imposta un risultato dell'azione (ObjectResult) con un codice di stato 422 Entità non elaborabile quando la negoziazione del contenuto ha esito negativo:

public class UnprocessableResultFilter : IAlwaysRunResultFilter
{
    public void OnResultExecuting(ResultExecutingContext context)
    {
        if (context.Result is StatusCodeResult statusCodeResult
            && statusCodeResult.StatusCode == StatusCodes.Status415UnsupportedMediaType)
        {
            context.Result = new ObjectResult("Unprocessable")
            {
                StatusCode = StatusCodes.Status422UnprocessableEntity
            };
        }
    }

    public void OnResultExecuted(ResultExecutedContext context) { }
}

IFilterFactory

IFilterFactory implementa IFilterMetadata. Pertanto, un'istanza di IFilterFactory può essere usata come un'istanza di IFilterMetadata in un punto qualsiasi della pipeline filtro. Quando il runtime si prepara per richiamare il filtro, cerca di eseguirne il cast a un oggetto IFilterFactory. Se l'esecuzione del cast ha esito positivo, viene chiamato il metodo CreateInstance per creare l'istanza di IFilterMetadata richiamata. In questo modo viene specificata una struttura flessibile, poiché non è necessario impostare in modo esplicito la pipeline filtro all'avvio dell'app.

IFilterFactory.IsReusable:

  • Suggerimento della factory che l'istanza del filtro creata dalla factory può essere riutilizzata all'esterno dell'ambito della richiesta in cui è stata creata.
  • Non deve essere usato con un filtro che dipende dai servizi con una durata diversa da singleton.

Il runtime di ASP.NET Core non garantisce:

  • Che venga creata una singola istanza del filtro.
  • Che il filtro non verrà richiesto di nuovo dal contenitore di inserimento delle dipendenze in un momento successivo.

Avviso

Configurare per restituire la restituzione IFilterFactory.IsReusabletrue solo se l'origine dei filtri non è ambigua, i filtri sono senza stato e i filtri sono sicuri da usare tra più richieste HTTP. Ad esempio, non restituire filtri dall'inserimento di dipendenze registrati come ambito o temporanei se IFilterFactory.IsReusable restituisce true.

Un altro approccio alla creazione di filtri consiste nell'implementare IFilterFactory usando implementazioni dell'attributo personalizzate:

public class ResponseHeaderFilterFactory : Attribute, IFilterFactory
{
    public bool IsReusable => false;

    public IFilterMetadata CreateInstance(IServiceProvider serviceProvider) =>
        new InternalResponseHeaderFilter();

    private class InternalResponseHeaderFilter : IActionFilter
    {
        public void OnActionExecuting(ActionExecutingContext context) =>
            context.HttpContext.Response.Headers.Add(
                nameof(OnActionExecuting), nameof(InternalResponseHeaderFilter));

        public void OnActionExecuted(ActionExecutedContext context) { }
    }

Il filtro viene applicato nel codice seguente:

[ResponseHeaderFilterFactory]
public IActionResult Index() =>
    Content($"- {nameof(FilterFactoryController)}.{nameof(Index)}");

Implementazione di IFilterFactory in un attributo

I filtri che implementano IFilterFactory sono utili per i filtri che:

  • Non richiedono il passaggio di parametri.
  • Hanno dipendenze del costruttore che devono essere soddisfatte dall'inserimento delle dipendenze.

TypeFilterAttribute implementa IFilterFactory. IFilterFactory espone il metodo CreateInstance per creare un'istanza IFilterMetadata. CreateInstance carica il tipo specificato dal contenitore di inserimento delle dipendenze.

public class SampleActionTypeFilterAttribute : TypeFilterAttribute
{
    public SampleActionTypeFilterAttribute()
         : base(typeof(InternalSampleActionFilter)) { }

    private class InternalSampleActionFilter : IActionFilter
    {
        private readonly ILogger<InternalSampleActionFilter> _logger;

        public InternalSampleActionFilter(ILogger<InternalSampleActionFilter> logger) =>
            _logger = logger;

        public void OnActionExecuting(ActionExecutingContext context)
        {
            _logger.LogInformation(
                $"- {nameof(InternalSampleActionFilter)}.{nameof(OnActionExecuting)}");
        }

        public void OnActionExecuted(ActionExecutedContext context)
        {
            _logger.LogInformation(
                $"- {nameof(InternalSampleActionFilter)}.{nameof(OnActionExecuted)}");
        }
    }
}

Il codice seguente illustra tre approcci per l'applicazione del filtro:

[SampleActionTypeFilter]
public IActionResult WithDirectAttribute() =>
    Content($"- {nameof(FilterFactoryController)}.{nameof(WithDirectAttribute)}");

[TypeFilter(typeof(SampleActionTypeFilterAttribute))]
public IActionResult WithTypeFilterAttribute() =>
    Content($"- {nameof(FilterFactoryController)}.{nameof(WithTypeFilterAttribute)}");

[ServiceFilter(typeof(SampleActionTypeFilterAttribute))]
public IActionResult WithServiceFilterAttribute() =>
    Content($"- {nameof(FilterFactoryController)}.{nameof(WithServiceFilterAttribute)}");

Nel codice precedente è preferibile il primo approccio all'applicazione del filtro.

Usare il middleware nella pipeline di filtro

I filtri risorse funzionano come middleware in quanto racchiudono l'esecuzione di tutto ciò che viene dopo nella pipeline. Tuttavia, i filtri differiscono dal middleware in quanto fanno parte del runtime, il che significa che hanno accesso al contesto e ai costrutti.

Per usare il middleware come filtro, creare un tipo con un metodo Configure che specifica il middleware da inserire nella pipeline di filtro. Nell'esempio seguente viene usato il middleware per impostare un'intestazione di risposta:

public class FilterMiddlewarePipeline
{
    public void Configure(IApplicationBuilder app)
    {
        app.Use(async (context, next) =>
        {
            context.Response.Headers.Add("Pipeline", "Middleware");

            await next();
        });
    }
}

Usare MiddlewareFilterAttribute per eseguire il middleware:

[MiddlewareFilter(typeof(FilterMiddlewarePipeline))]
public class FilterMiddlewareController : Controller
{
    public IActionResult Index() =>
        Content($"- {nameof(FilterMiddlewareController)}.{nameof(Index)}");
}

I filtri middleware vengono eseguiti nella stessa fase della pipeline filtro come filtri risorse, prima dell'associazione di modelli e dopo il resto della pipeline.

Thread safety

Quando si passa un'istanza di un filtro in Add, anziché il relativo Type, il filtro è un singleton e non è thread-safe.

Risorse aggiuntive

Di Kirk Larkin, Rick Anderson, Tom Dykstra e Steve Smith

I filtri in ASP.NET Core consentono l'esecuzione del codice prima o dopo fasi specifiche della pipeline di elaborazione della richiesta.

I filtri predefiniti gestiscono attività, ad esempio:

  • Autorizzazione, che impedisce l'accesso alle risorse per cui un utente non è autorizzato.
  • Memorizzazione nella cache delle risposte, corto circuito della pipeline di richiesta per restituire una risposta memorizzata nella cache.

I filtri personalizzati possono essere creati per gestire problemi relativi a più settori. Esempi di problematiche trasversali includono la gestione degli errori, la memorizzazione nella cache, la configurazione, l'autorizzazione e la registrazione. I filtri evitano la duplicazione del codice. Ad esempio, un filtro eccezioni per la gestione degli errori potrebbe consolidare la gestione degli errori.

Questo documento si applica a Razor Pagine, controller API e controller con visualizzazioni. I filtri non funzionano direttamente con i Razor componenti. Un filtro può influire indirettamente su un componente quando:

  • Il componente è incorporato in una pagina o in una visualizzazione.
  • La pagina o il controller e la visualizzazione usano il filtro.

Visualizzare o scaricare un esempio (procedura per il download).

Funzionamento dei filtri

I filtri vengono eseguiti all'interno della pipeline di chiamata di azioni ASP.NET Core, talvolta definita pipeline di filtro. La pipeline di filtro viene eseguita dopo che ASP.NET Core ha selezionato l'azione da eseguire.

The request is processed through Other Middleware, Routing Middleware, Action Selection, and the Action Invocation Pipeline. The request processing continues back through Action Selection, Routing Middleware, and various Other Middleware before becoming a response sent to the client.

Tipi di filtri

Ogni tipo di filtro viene eseguito in una fase diversa della pipeline di filtro:

  • I filtri di autorizzazione vengono eseguiti per primi e consentono di determinare se l'utente è autorizzato per la richiesta. I filtri di autorizzazione causano un corto circuito della pipeline se la richiesta non è autorizzata.

  • Filtri di risorse:

    • Vengono eseguiti dopo l'autorizzazione.
    • OnResourceExecuting esegue il codice prima del resto della pipeline del filtro. Ad esempio, OnResourceExecuting esegue il codice prima dell'associazione di modelli.
    • OnResourceExecuted esegue il codice dopo il completamento del resto della pipeline.
  • Filtri azione:

    • Eseguire il codice immediatamente prima e dopo la chiamata di un metodo di azione.
    • Può modificare gli argomenti passati in un'azione.
    • Può modificare il risultato restituito dall'azione.
    • Non sono supportati in Razor Pages.
  • I filtri eccezioni applicano criteri globali a eccezioni non gestite che si verificano prima che il corpo della risposta sia stato scritto in.

  • I filtri dei risultati eseguono il codice immediatamente prima e dopo l'esecuzione dei risultati dell'azione. Vengono eseguiti solo quando il metodo di azione è stato eseguito correttamente. Sono utili per la logica che deve racchiudere l'esecuzione del formattatore o di una vista.

Il diagramma seguente illustra come interagiscono i tipi di filtro nella pipeline di filtro.

The request is processed through Authorization Filters, Resource Filters, Model Binding, Action Filters, Action Execution and Action Result Conversion, Exception Filters, Result Filters, and Result Execution. On the way out, the request is only processed by Result Filters and Resource Filters before becoming a response sent to the client.

Implementazione

I filtri supportano sia implementazioni sincrone che asincrone tramite definizioni di interfaccia diverse.

I filtri sincroni eseguono codice prima e dopo la fase della pipeline. La chiamata di OnActionExecuting, ad esempio, avviene prima della chiamata del metodo di azione. La chiamata di OnActionExecuted avviene dopo l'esecuzione del metodo di azione.

public class MySampleActionFilter : IActionFilter 
{
    public void OnActionExecuting(ActionExecutingContext context)
    {
        // Do something before the action executes.
        MyDebug.Write(MethodBase.GetCurrentMethod(), context.HttpContext.Request.Path);
    }

    public void OnActionExecuted(ActionExecutedContext context)
    {
        // Do something after the action executes.
        MyDebug.Write(MethodBase.GetCurrentMethod(), context.HttpContext.Request.Path);
    }
}

Nel codice precedente MyDebug è una funzione di utilità nel download di esempio.

I filtri asincroni definiscono un On-Stage-ExecutionAsync metodo. Ad esempio, OnActionExecutionAsync:

public class SampleAsyncActionFilter : IAsyncActionFilter
{
    public async Task OnActionExecutionAsync(
        ActionExecutingContext context,
        ActionExecutionDelegate next)
    {
        // Do something before the action executes.

        // next() calls the action method.
        var resultContext = await next();
        // resultContext.Result is set.
        // Do something after the action executes.
    }
}

Nel codice precedente SampleAsyncActionFilter ha un oggetto ActionExecutionDelegate (next) che esegue il metodo di azione.

Fasi di filtro multiple

È possibile implementare interfacce per più fasi di filtro in una singola classe. Ad esempio, la ActionFilterAttribute classe implementa:

Implementare la versione sincrona oppure la versione asincrona di un'interfaccia di filtro, non entrambe. Il runtime controlla per prima cosa se il filtro implementa l'interfaccia asincrona e, in tal caso, la chiama. In caso contrario, chiama i metodi dell'interfaccia sincrona. Se in una classe sono implementate entrambe le interfacce, sincrona e asincrona, viene chiamato solo il metodo asincrono. Quando si usano classi astratte come ActionFilterAttribute, eseguire l'override solo dei metodi sincroni o dei metodi asincroni per ogni tipo di filtro.

Attributi filtro predefiniti

ASP.NET Core include filtri predefiniti basati su attributi che è possibile impostare come sottoclasse e personalizzare. Il filtro dei risultati seguente, ad esempio, aggiunge un'intestazione alla risposta:

public class AddHeaderAttribute : ResultFilterAttribute
{
    private readonly string _name;
    private readonly string _value;

    public AddHeaderAttribute(string name, string value)
    {
        _name = name;
        _value = value;
    }

    public override void OnResultExecuting(ResultExecutingContext context)
    {
        context.HttpContext.Response.Headers.Add( _name, new string[] { _value });
        base.OnResultExecuting(context);
    }
}

Gli attributi consentono ai filtri di accettare argomenti, come illustrato nell'esempio precedente. Applicare AddHeaderAttribute a un controller o a un metodo di azione e specificare il nome e il valore dell'intestazione HTTP:

[AddHeader("Author", "Rick Anderson")]
public class SampleController : Controller
{
    public IActionResult Index()
    {
        return Content("Examine the headers using the F12 developer tools.");
    }

Usare uno strumento come gli strumenti di sviluppo del browser per esaminare le intestazioni. In Intestazioniauthor: Rick Anderson di risposta viene visualizzato.

Il codice seguente implementa un oggetto ActionFilterAttribute che:

  • Legge il titolo e il nome dal sistema di configurazione. A differenza dell'esempio precedente, il codice seguente non richiede l'aggiunta di parametri di filtro al codice.
  • Aggiunge il titolo e il nome all'intestazione della risposta.
public class MyActionFilterAttribute : ActionFilterAttribute
{
    private readonly PositionOptions _settings;

    public MyActionFilterAttribute(IOptions<PositionOptions> options)
    {
        _settings = options.Value;
        Order = 1;
    }

    public override void OnResultExecuting(ResultExecutingContext context)
    {
        context.HttpContext.Response.Headers.Add(_settings.Title, 
                                                 new string[] { _settings.Name });
        base.OnResultExecuting(context);
    }
}

Le opzioni di configurazione vengono fornite dal sistema di configurazione usando il modello di opzioni. Ad esempio, dal appsettings.json file :

{
  "Position": {
    "Title": "Editor",
    "Name": "Joe Smith"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*"
}

StartUp.ConfigureServicesIn :

  • La PositionOptions classe viene aggiunta al contenitore del servizio con l'area "Position" di configurazione.
  • L'oggetto MyActionFilterAttribute viene aggiunto al contenitore del servizio.
public void ConfigureServices(IServiceCollection services)
{
    services.Configure<PositionOptions>(
             Configuration.GetSection("Position"));
    services.AddScoped<MyActionFilterAttribute>();

    services.AddControllersWithViews();
}

Il codice seguente illustra la PositionOptions classe :

public class PositionOptions
{
    public string Title { get; set; }
    public string Name { get; set; }
}

Il codice seguente applica l'oggetto MyActionFilterAttribute al Index2 metodo :

[AddHeader("Author", "Rick Anderson")]
public class SampleController : Controller
{
    public IActionResult Index()
    {
        return Content("Examine the headers using the F12 developer tools.");
    }

    [ServiceFilter(typeof(MyActionFilterAttribute))]
    public IActionResult Index2()
    {
        return Content("Header values by configuration.");
    }

In Intestazioni di risposta , author: Rick Andersone Editor: Joe Smith viene visualizzato quando viene chiamato l'endpoint Sample/Index2 .

Il codice seguente applica MyActionFilterAttribute e alla AddHeaderAttributeRazor pagina:

[AddHeader("Author", "Rick Anderson")]
[ServiceFilter(typeof(MyActionFilterAttribute))]
public class IndexModel : PageModel
{
    public void OnGet()
    {
    }
}

I filtri non possono essere applicati ai Razor metodi del gestore Page. Possono essere applicati al Razor modello Page o a livello globale.

Molte delle interfacce del filtro presentano attributi corrispondenti che possono essere usati come classi di base per implementazioni personalizzate.

Attributi dei filtri:

Ambiti dei filtri e ordine di esecuzione

È possibile aggiungere un filtro alla pipeline in uno dei tre ambiti:

  • Uso di un attributo in un'azione del controller. Gli attributi di filtro non possono essere applicati ai Razor metodi del gestore Pages.
  • Uso di un attributo in un controller o Razor in una pagina.
  • A livello globale per tutti i controller, le azioni e Razor le pagine, come illustrato nel codice seguente:
public void ConfigureServices(IServiceCollection services)
{
    services.AddControllersWithViews(options =>
   {
        options.Filters.Add(typeof(MySampleActionFilter));
    });
}

Ordine di esecuzione predefinito

Quando sono presenti più filtri per una particolare fase della pipeline, l'ambito determina l'ordine di esecuzione predefinito dei filtri. I filtri globali racchiudono i filtri di classe, che a loro volta racchiudono i filtri dei metodi.

Come risultato dell'annidamento dei filtri, il codice after dei filtri viene eseguito in ordine inverso rispetto al codice before. Sequenza di filtro:

  • Codice before dei filtri globali.
    • Codice precedente dei filtri controller e Razor Pagina.
      • Codice before dei filtri del metodo di azione.
      • Codice after dei filtri del metodo di azione.
    • Codice successivo dei filtri controller e Razor Pagina.
  • Codice after dei filtri globali.

L'esempio seguente illustra l'ordine in cui i metodi dei filtri vengono chiamati per i filtri di azione sincroni.

Sequence Ambito del filtro Metodo del filtro
1 Generale OnActionExecuting
2 Controller o Razor pagina OnActionExecuting
3 Method OnActionExecuting
4 Method OnActionExecuted
5 Controller o Razor pagina OnActionExecuted
6 Generale OnActionExecuted

Filtri a livello di controller

Ogni controller che eredita dalla Controller classe base include Controller.OnActionExecutingmetodi , Controller.OnActionExecutionAsynce Controller.OnActionExecutedOnActionExecuted . Questi metodi:

  • Eseguono il wrapping dei filtri che vengono eseguiti per una determinata azione.
  • La chiamata di OnActionExecuting avviene prima di qualsiasi filtro dell'azione.
  • La chiamata di OnActionExecuted avviene dopo tutti i filtri dell'azione.
  • La chiamata di OnActionExecutionAsync avviene prima di qualsiasi filtro dell'azione. Il codice del filtro dopo next viene eseguito dopo il metodo di azione.

Ad esempio, l'applicazione di MySampleActionFilter nell'esempio scaricato avviene a livello globale all'avvio.

TestController:

  • Applica l'oggetto SampleActionFilterAttribute ([SampleActionFilter]) all'azione FilterTest2 .
  • Esegue l'override di OnActionExecuting e OnActionExecuted.
public class TestController : Controller
{
    [SampleActionFilter(Order = int.MinValue)]
    public IActionResult FilterTest2()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }

    public override void OnActionExecuting(ActionExecutingContext context)
    {
        // Do something before the action executes.
        MyDebug.Write(MethodBase.GetCurrentMethod(), HttpContext.Request.Path);
        base.OnActionExecuting(context);
    }

    public override void OnActionExecuted(ActionExecutedContext context)
    {
        // Do something after the action executes.
        MyDebug.Write(MethodBase.GetCurrentMethod(), HttpContext.Request.Path);
        base.OnActionExecuted(context);
    }
}

MyDisplayRouteInfo viene fornito dal pacchetto NuGet Rick.Docs.Samples.RouteInfo e visualizza le informazioni sulla route.

Passando a https://localhost:5001/Test/FilterTest2, viene eseguito il codice seguente:

  • TestController.OnActionExecuting
    • MySampleActionFilter.OnActionExecuting
      • SampleActionFilterAttribute.OnActionExecuting
        • TestController.FilterTest2
      • SampleActionFilterAttribute.OnActionExecuted
    • MySampleActionFilter.OnActionExecuted
  • TestController.OnActionExecuted

I filtri a livello di controller impostano la proprietà Order su int.MinValue. Non è possibile impostare filtri a livello di controller per l'esecuzione dopo l'applicazione dei filtri ai metodi. L'ordine è illustrato nella sezione successiva.

Per Razor le pagine, vedere Implementare Razor filtri di pagina eseguendo l'override dei metodi di filtro.

Override dell'ordine predefinito

È possibile eseguire l'override della sequenza di esecuzione predefinita implementando IOrderedFilter. IOrderedFilter espone la proprietà Order, che ha la precedenza sull'ambito per determinare l'ordine di esecuzione. Un filtro con un valore di Order inferiore:

  • Esegue il codice before prima di quello di un filtro con un valore di Order più alto.
  • Esegue il codice after dopo quello di un filtro con un valore di Order più alto.

La Order proprietà viene impostata con un parametro del costruttore:

[SampleActionFilter(Order = int.MinValue)]

Considerare i due filtri azione nel controller seguente:

[MyAction2Filter]
public class Test2Controller : Controller
{
    public IActionResult FilterTest2()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }

    public override void OnActionExecuting(ActionExecutingContext context)
    {
        // Do something before the action executes.
        MyDebug.Write(MethodBase.GetCurrentMethod(), HttpContext.Request.Path);
        base.OnActionExecuting(context);
    }

    public override void OnActionExecuted(ActionExecutedContext context)
    {
        // Do something after the action executes.
        MyDebug.Write(MethodBase.GetCurrentMethod(), HttpContext.Request.Path);
        base.OnActionExecuted(context);
    }
}

Un filtro globale viene aggiunto in StartUp.ConfigureServices:

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllersWithViews(options =>
   {
        options.Filters.Add(typeof(MySampleActionFilter));
    });
}

I 3 filtri vengono eseguiti nell'ordine seguente:

  • Test2Controller.OnActionExecuting
    • MySampleActionFilter.OnActionExecuting
      • MyAction2FilterAttribute.OnActionExecuting
        • Test2Controller.FilterTest2
      • MyAction2FilterAttribute.OnResultExecuting
    • MySampleActionFilter.OnActionExecuted
  • Test2Controller.OnActionExecuted

La proprietà Order ha la precedenza sull'ambito nel determinare l'ordine di esecuzione dei filtri. I filtri vengono ordinati prima in base all'ordine, poi viene usato l'ambito per interrompere i collegamenti. Tutti i filtri predefiniti implementano IOrderedFilter e impostano il valore predefinito di Order su 0. Come accennato in precedenza, i filtri a livello di controller impostano la proprietà Order su int.MinValue Per i filtri predefiniti, l'ambito determina l'ordine a meno che non sia impostato su un valore diverso da Order zero.

Nel codice precedente, MySampleActionFilter ha un ambito globale in modo che venga eseguito prima MyAction2FilterAttributedi , con ambito controller. Per eseguire MyAction2FilterAttribute prima di tutto, impostare l'ordine su int.MinValue:

[MyAction2Filter(int.MinValue)]
public class Test2Controller : Controller
{
    public IActionResult FilterTest2()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }

    public override void OnActionExecuting(ActionExecutingContext context)
    {
        // Do something before the action executes.
        MyDebug.Write(MethodBase.GetCurrentMethod(), HttpContext.Request.Path);
        base.OnActionExecuting(context);
    }

    public override void OnActionExecuted(ActionExecutedContext context)
    {
        // Do something after the action executes.
        MyDebug.Write(MethodBase.GetCurrentMethod(), HttpContext.Request.Path);
        base.OnActionExecuted(context);
    }
}

Per eseguire prima il filtro MySampleActionFilter globale, impostare su Orderint.MinValue:

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllersWithViews(options =>
   {
        options.Filters.Add(typeof(MySampleActionFilter),
                            int.MinValue);
    });
}

Annullamento e corto circuito

È possibile causare il corto circuito della pipeline di filtro impostando la proprietà Result sul parametro ResourceExecutingContext fornito al metodo di filtro. Ad esempio, il filtro di risorse seguente impedisce l'esecuzione del resto della pipeline:

public class ShortCircuitingResourceFilterAttribute : Attribute, IResourceFilter
{
    public void OnResourceExecuting(ResourceExecutingContext context)
    {
        context.Result = new ContentResult()
        {
            Content = "Resource unavailable - header not set."
        };
    }

    public void OnResourceExecuted(ResourceExecutedContext context)
    {
    }
}

Nel codice seguente sia il filtro ShortCircuitingResourceFilter che il filtro AddHeader hanno come destinazione il metodo di azione SomeResource. ShortCircuitingResourceFilter:

  • Viene eseguito per primo perché è un filtro risorsa e AddHeader è un filtro azione.
  • Blocca il resto della pipeline.

Pertanto il filtro AddHeader non viene mai eseguito per l'azione SomeResource. Questo comportamento sarebbe lo stesso se entrambi i filtri venissero applicati a livello di metodo di azione, a condizione che il filtro ShortCircuitingResourceFilter venga eseguito prima. ShortCircuitingResourceFilter viene eseguito per primo per il tipo di filtro o per l'uso esplicito della proprietà Order.

[AddHeader("Author", "Rick Anderson")]
public class SampleController : Controller
{
    public IActionResult Index()
    {
        return Content("Examine the headers using the F12 developer tools.");
    }

    [ServiceFilter(typeof(MyActionFilterAttribute))]
    public IActionResult Index2()
    {
        return Content("Header values by configuration.");
    }

    [ShortCircuitingResourceFilter]
    public IActionResult SomeResource()
    {
        return Content("Successful access to resource - header is set.");
    }

    [AddHeaderWithFactory]
    public IActionResult HeaderWithFactory()
    {
        return Content("Examine the headers using the F12 developer tools.");
    }
}

Inserimento delle dipendenze

È possibile aggiungere filtri per tipo o per istanza. Se viene aggiunta un'istanza, tale istanza viene usata per ogni richiesta. Se viene aggiunto un tipo, il filtro è attivato dal tipo. Un filtro attivato dal tipo comporta:

  • La creazione di un'istanza per ogni richiesta.
  • Il popolamento di tutte le dipendenze del costruttore tramite inserimento delle dipendenze.

I filtri implementati come attributi e aggiunti direttamente alle classi controller o ai metodi di azione non possono avere dipendenze costruttore specificate dall'inserimento di dipendenze. Le dipendenze del costruttore non possono essere fornite tramite inserimento delle dipendenze in quanto:

  • I parametri del costruttore devono essere forniti agli attributi nella posizione in cui vengono applicati.
  • Questa è una limitazione del funzionamento degli attributi.

I filtri seguenti supportano le dipendenze del costruttore fornite dall'inserimento delle dipendenze:

I filtri precedenti possono essere applicati a un controller o a un metodo di azione:

I logger sono disponibili tramite l'inserimento delle dipendenze. Evitare tuttavia di creare e usare filtri esclusivamente per scopi di registrazione. La funzionalità di registrazione del framework predefinita in genere fornisce il necessario per la registrazione. La registrazione aggiunta ai filtri:

  • Deve concentrarsi su problemi del dominio di business o comportamenti specifici del filtro.
  • Non deve registrare azioni o altri eventi del framework. L'oggetto predefinito filtra le azioni di log e gli eventi del framework.

ServiceFilterAttribute

I tipi di implementazione del filtro di servizi sono registrati in ConfigureServices. ServiceFilterAttribute recupera un'istanza del filtro dall'inserimento di dipendenze.

Il codice seguente illustra AddHeaderResultServiceFilter:

public class AddHeaderResultServiceFilter : IResultFilter
{
    private ILogger _logger;
    public AddHeaderResultServiceFilter(ILoggerFactory loggerFactory)
    {
        _logger = loggerFactory.CreateLogger<AddHeaderResultServiceFilter>();
    }

    public void OnResultExecuting(ResultExecutingContext context)
    {
        var headerName = "OnResultExecuting";
        context.HttpContext.Response.Headers.Add(
            headerName, new string[] { "ResultExecutingSuccessfully" });
        _logger.LogInformation("Header added: {HeaderName}", headerName);
    }

    public void OnResultExecuted(ResultExecutedContext context)
    {
        // Can't add to headers here because response has started.
        _logger.LogInformation("AddHeaderResultServiceFilter.OnResultExecuted");
    }
}

Nel codice seguente l'oggetto AddHeaderResultServiceFilter viene aggiunto al contenitore di inserimento delle dipendenze:

public void ConfigureServices(IServiceCollection services)
{
    // Add service filters.
    services.AddScoped<AddHeaderResultServiceFilter>();
    services.AddScoped<SampleActionFilterAttribute>();

    services.AddControllersWithViews(options =>
   {
       options.Filters.Add(new AddHeaderAttribute("GlobalAddHeader",
           "Result filter added to MvcOptions.Filters"));         // An instance
        options.Filters.Add(typeof(MySampleActionFilter));         // By type
        options.Filters.Add(new SampleGlobalActionFilter());       // An instance
    });
}

Nel codice seguente l'attributo ServiceFilter recupera un'istanza del filtro AddHeaderResultServiceFilter dall'inserimento delle dipendenze:

[ServiceFilter(typeof(AddHeaderResultServiceFilter))]
public IActionResult Index()
{
    return View();
}

Quando si usa ServiceFilterAttribute, impostando ServiceFilterAttribute.IsReusable:

  • Indica che l'istanza del filtro può essere riutilizzata all'esterno dell'ambito della richiesta in cui è stata creata. Il runtime di ASP.NET Core non garantisce:

    • Che venga creata una singola istanza del filtro.
    • Che il filtro non verrà richiesto di nuovo dal contenitore di inserimento delle dipendenze in un momento successivo.
  • Non si deve usare con un filtro che dipende da servizi con una durata diversa da singleton.

ServiceFilterAttribute implementa IFilterFactory. IFilterFactory espone il metodo CreateInstance per creare un'istanza IFilterMetadata. CreateInstance carica il tipo specificato dall'inserimento delle dipendenze.

TypeFilterAttribute

TypeFilterAttribute è simile a ServiceFilterAttribute, ma il relativo tipo non viene risolto direttamente dal contenitore dell'inserimento di dipendenze. Viene creata un'istanza del tipo tramite Microsoft.Extensions.DependencyInjection.ObjectFactory.

Poiché i tipi TypeFilterAttribute non vengono risolti direttamente dal contenitore di inserimento delle dipendenze:

  • Non è necessario che i tipi a cui viene fatto riferimento tramite TypeFilterAttribute siano registrati nel contenitore di inserimento delle dipendenze. Le loro dipendenze vengono soddisfatte dal contenitore di inserimento delle dipendenze.
  • In via facoltativa, TypeFilterAttribute può anche accettare gli argomenti del costruttore per il tipo.

Quando si usa TypeFilterAttribute, impostando TypeFilterAttribute.IsReusable:

  • Indica che l'istanza del filtro può essere riutilizzata all'esterno dell'ambito della richiesta in cui è stata creata. Il runtime di ASP.NET Core non fornisce alcuna garanzia che venga creata una singola istanza del filtro.

  • Non si deve usare con un filtro che dipende da servizi con una durata diversa da singleton.

L'esempio seguente illustra come passare argomenti a un tipo usando TypeFilterAttribute:

[TypeFilter(typeof(LogConstantFilter),
    Arguments = new object[] { "Method 'Hi' called" })]
public IActionResult Hi(string name)
{
    return Content($"Hi {name}");
}

Filtri autorizzazione

I filtri di autorizzazione:

  • Sono i primi filtri a essere eseguiti nella pipeline di filtro.
  • Controllano l'accesso ai metodi di azione.
  • Dispongono di un metodo precedente, ma non di un metodo successivo.

I filtri di autorizzazione personalizzati richiedono un framework di autorizzazione personalizzato. È preferibile configurare criteri di autorizzazione o scrivere criteri di autorizzazione personalizzati piuttosto che scrivere un filtro personalizzato. Il filtro di autorizzazione predefinito:

  • Chiama il sistema di autorizzazione.
  • Non autorizza le richieste.

Non generare eccezioni nei filtri di autorizzazione:

  • L'eccezione non verrà gestita.
  • I filtri di eccezione non gestiranno l'eccezione.

È consigliabile emettere una richiesta di verifica in caso di eccezione in un filtro di autorizzazione.

Altre informazioni sull'autorizzazione.

Filtri risorse

I filtri di risorse:

I filtri di risorse sono utili per causare il corto circuito della maggior parte della pipeline. Un filtro di memorizzazione nella cache può, ad esempio, evitare il resto della pipeline in caso di riscontro nella cache.

Esempi di filtri di risorse:

Filtri azione

I filtri azione non si applicano alle Razor pagine. Razor Pages supporta IPageFilter e IAsyncPageFilter. Per altre informazioni, vedere Metodi di filtro per Razor Pagine.

I filtri di azione:

Il codice seguente mostra un filtro di azione di esempio:

public class MySampleActionFilter : IActionFilter 
{
    public void OnActionExecuting(ActionExecutingContext context)
    {
        // Do something before the action executes.
        MyDebug.Write(MethodBase.GetCurrentMethod(), context.HttpContext.Request.Path);
    }

    public void OnActionExecuted(ActionExecutedContext context)
    {
        // Do something after the action executes.
        MyDebug.Write(MethodBase.GetCurrentMethod(), context.HttpContext.Request.Path);
    }
}

La classe ActionExecutingContext specifica le proprietà seguenti:

  • ActionArguments : consente di leggere gli input in un metodo di azione.
  • Controller: consente di modificare l'istanza del controller.
  • Result: l'impostazione di Result causa il corto circuito del metodo di azione e dei filtri di azione successivi.

La generazione di un'eccezione in un metodo di azione:

  • Impedisce l'esecuzione dei filtri successivi.
  • A differenza dell'impostazione di Result, è considerata un errore anziché un risultato positivo.

La classe ActionExecutedContext specifica Controller e Result oltre alle proprietà seguenti:

  • Canceled: true se un altro file ha causato il corto circuito dell'azione.

  • Exception: non Null se l'azione o un filtro di azione eseguito in precedenza ha generato un'eccezione. Se si imposta questa proprietà su Null:

    • L'eccezione viene gestita in modo efficace.
    • L'oggetto Result viene eseguito come se fosse stato restituito dal metodo di azione.

Per un oggetto IAsyncActionFilter, una chiamata a ActionExecutionDelegate:

  • Esegue qualsiasi filtro azione successivo e il metodo di azione.
  • Restituisce ActionExecutedContext.

Per causare il corto circuito, assegnare Microsoft.AspNetCore.Mvc.Filters.ActionExecutingContext.Result a un'istanza del risultato e non chiamare next (ActionExecutionDelegate).

Il framework fornisce un oggetto ActionFilterAttribute astratto che è possibile impostare come sottoclasse.

Il filtro di azione OnActionExecuting può essere usato per:

  • Convalidare lo stato del modello.
  • Restituire un errore se lo stato non è valido.
public class ValidateModelAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext 
                                           context)
    {
        if (!context.ModelState.IsValid)
        {
            context.Result = new BadRequestObjectResult(
                                                context.ModelState);
        }
    }

Nota

I controller annotati con l'attributo convalidano automaticamente lo stato del [ApiController] modello e restituiscono una risposta 400. Per altre informazioni, vedere Risposte HTTP 400 automatiche. Il metodo OnActionExecuted viene eseguito dopo il metodo di azione:

  • È possibile visualizzare e modificare i risultati dell'azione tramite la proprietà Result.

  • L'impostazione di Canceled è true se un altro filtro ha causato il corto circuito dell'esecuzione dell'azione.

  • L'impostazione di Exception è un valore non Null se l'azione o un filtro di azione successivo ha generato un'eccezione. Impostazione di Exception su null:

    • Gestisce un'eccezione in modo efficace.
    • L'oggetto ActionExecutedContext.Result viene eseguito come se fosse stato restituito normalmente dal metodo di azione.
public class ValidateModelAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext 
                                           context)
    {
        if (!context.ModelState.IsValid)
        {
            context.Result = new BadRequestObjectResult(
                                                context.ModelState);
        }
    }


    public override void OnActionExecuted(ActionExecutedContext 
                                          context)
    {
        var result = context.Result;
        // Do something with Result.
        if (context.Canceled == true)
        {
            // Action execution was short-circuited by another filter.
        }

        if(context.Exception != null)
        {
            // Exception thrown by action or action filter.
            // Set to null to handle the exception.
            context.Exception = null;
        }
        base.OnActionExecuted(context);
    }
}

Filtri eccezioni

Filtri eccezioni:

Il filtro di eccezione di esempio seguente usa una visualizzazione degli errori personalizzata per mostrare i dettagli sulle eccezioni che si verificano quando l'app è in fase di sviluppo:

public class CustomExceptionFilter : IExceptionFilter
{
    private readonly IWebHostEnvironment _hostingEnvironment;
    private readonly IModelMetadataProvider _modelMetadataProvider;

    public CustomExceptionFilter(
        IWebHostEnvironment hostingEnvironment,
        IModelMetadataProvider modelMetadataProvider)
    {
        _hostingEnvironment = hostingEnvironment;
        _modelMetadataProvider = modelMetadataProvider;
    }

    public void OnException(ExceptionContext context)
    {
        if (!_hostingEnvironment.IsDevelopment())
        {
            return;
        }
        var result = new ViewResult {ViewName = "CustomError"};
        result.ViewData = new ViewDataDictionary(_modelMetadataProvider,
                                                    context.ModelState);
        result.ViewData.Add("Exception", context.Exception);
        // TODO: Pass additional detailed data via ViewData
        context.Result = result;
    }
}

Il codice seguente verifica il filtro delle eccezioni:

[TypeFilter(typeof(CustomExceptionFilter))]
public class FailingController : Controller
{
    [AddHeader("Failing Controller", 
               "Won't appear when exception is handled")]
    public IActionResult Index()
    {
        throw new Exception("Testing custom exception filter.");
    }
}

Filtri eccezioni:

  • Non dispone di eventi precedenti o successivi.
  • Implementano OnException o OnExceptionAsync.
  • Gestire le eccezioni non gestite che si verificano nella creazione di pagine o controller, nell'associazione Razor di modelli, nei filtri di azione o nei metodi di azione.
  • Non rilevano le eccezioni che si verificano nei filtri di risorse, nei filtri dei risultati o nell'esecuzione dei risultati MVC.

Per gestire un'eccezione, impostare la ExceptionHandled proprietà su true o assegnare la Result proprietà . In questo modo si arresta la propagazione dell'eccezione. Un filtro di eccezione non può modificare un'eccezione in una "operazione riuscita". Questa operazione può essere eseguita solo tramite un filtro di azione.

Filtri eccezioni:

  • Sono ideali per intercettare le eccezioni che si verificano nelle azioni.
  • Non sono flessibili quanto il middleware di gestione degli errori.

Scegliere il middleware per la gestione delle eccezioni. Usare i filtri di eccezione solo quando la gestione degli errori varia in base al metodo di azione chiamato. Un'app può ad esempio avere metodi di azione sia per gli endpoint API che per le visualizzazioni/HTML. Gli endpoint API potrebbero restituire informazioni sull'errore come JSON, mentre le azioni basate sulla visualizzazione potrebbero restituire una pagina di errore come HTML.

Filtri risultato

I filtri dei risultati:

IResultFilter e IAsyncResultFilter

Il codice seguente illustra un filtro dei risultati che aggiunge un'intestazione HTTP:

public class AddHeaderResultServiceFilter : IResultFilter
{
    private ILogger _logger;
    public AddHeaderResultServiceFilter(ILoggerFactory loggerFactory)
    {
        _logger = loggerFactory.CreateLogger<AddHeaderResultServiceFilter>();
    }

    public void OnResultExecuting(ResultExecutingContext context)
    {
        var headerName = "OnResultExecuting";
        context.HttpContext.Response.Headers.Add(
            headerName, new string[] { "ResultExecutingSuccessfully" });
        _logger.LogInformation("Header added: {HeaderName}", headerName);
    }

    public void OnResultExecuted(ResultExecutedContext context)
    {
        // Can't add to headers here because response has started.
        _logger.LogInformation("AddHeaderResultServiceFilter.OnResultExecuted");
    }
}

Il tipo di risultato eseguito dipende dall'azione. Un'azione che restituisce una visualizzazione include tutta l'elaborazione razor come parte dell'esecuzione ViewResult . Un metodo API può eseguire la serializzazione in quanto parte dell'esecuzione del risultato. Altre informazioni sui risultati delle azioni.

I filtri dei risultati vengono eseguiti solo quando un'azione o un filtro azione produce un risultato dell'azione. I filtri dei risultati non vengono eseguiti quando:

  • Un filtro di autorizzazione o un filtro di risorse cortocircuita la pipeline.
  • Un filtro di eccezione non gestisca un'eccezione producendo un risultato dell'azione.

Il metodo Microsoft.AspNetCore.Mvc.Filters.IResultFilter.OnResultExecuting può causare il corto circuito dell'esecuzione del risultato dell'azione e dei filtri dei risultati successivi se si imposta Microsoft.AspNetCore.Mvc.Filters.ResultExecutingContext.Cancel su true. In caso di corto circuito, scrivere nell'oggetto risposta per evitare di generare una risposta vuota. Generazione di un'eccezione in IResultFilter.OnResultExecuting:

  • Impedisce l'esecuzione del risultato dell'azione e dei filtri successivi.
  • Viene considerato come un errore anziché come risultato positivo.

Quando il Microsoft.AspNetCore.Mvc.Filters.IResultFilter.OnResultExecuted metodo viene eseguito, è probabile che la risposta sia già stata inviata al client. Se la risposta è già stata inviata al client, non può essere modificata.

L'impostazione di ResultExecutedContext.Canceled è true se un altro filtro ha causato il corto circuito dell'esecuzione del risultato dell'azione.

L'impostazione di ResultExecutedContext.Exception è un valore non Null se il risultato dell'azione o un filtro dei risultati successivo ha generato un'eccezione. L'impostazione su Exception Null gestisce in modo efficace un'eccezione e impedisce che l'eccezione venga generata nuovamente più avanti nella pipeline. Non c'è alcun modo affidabile per scrivere i dati in una risposta quando si gestisce un'eccezione in un filtro dei risultati. Se le intestazioni sono state scaricate nel client quando il risultato di un'azione genera un'eccezione, non c'è alcun meccanismo affidabile per inviare un codice di errore.

Per un oggetto IAsyncResultFilter, una chiamata a await next in ResultExecutionDelegate esegue tutti i filtri dei risultati successivi e il risultato dell'azione. Per corto circuito, impostare su ResultExecutingContext.Canceltrue e non chiamare :ResultExecutionDelegate

public class MyAsyncResponseFilter : IAsyncResultFilter
{
    public async Task OnResultExecutionAsync(ResultExecutingContext context,
                                             ResultExecutionDelegate next)
    {
        if (!(context.Result is EmptyResult))
        {
            await next();
        }
        else
        {
            context.Cancel = true;
        }

    }
}

Il framework fornisce un oggetto ResultFilterAttribute astratto che è possibile impostare come sottoclasse. La classe AddHeaderAttribute illustrata in precedenza è un esempio di un attributo di filtro dei risultati.

IAlwaysRunResultFilter e IAsyncAlwaysRunResultFilter

Le interfacce IAlwaysRunResultFilter e IAsyncAlwaysRunResultFilter dichiarano un'implementazione di IResultFilter eseguita per tutti i risultati dell'azione. Sono inclusi i risultati dell'azione prodotti da:

  • Filtri di autorizzazione e filtri delle risorse che causano un corto circuito.
  • Filtri eccezioni.

Ad esempio, il filtro seguente viene eseguito sempre e imposta un risultato dell'azione (ObjectResult) con un codice di stato 422 Entità non elaborabile quando la negoziazione del contenuto ha esito negativo:

public class UnprocessableResultFilter : Attribute, IAlwaysRunResultFilter
{
    public void OnResultExecuting(ResultExecutingContext context)
    {
        if (context.Result is StatusCodeResult statusCodeResult &&
            statusCodeResult.StatusCode == (int) HttpStatusCode.UnsupportedMediaType)
        {
            context.Result = new ObjectResult("Can't process this!")
            {
                StatusCode = (int) HttpStatusCode.UnsupportedMediaType,
            };
        }
    }

    public void OnResultExecuted(ResultExecutedContext context)
    {
    }
}

IFilterFactory

IFilterFactory implementa IFilterMetadata. Pertanto, un'istanza di IFilterFactory può essere usata come un'istanza di IFilterMetadata in un punto qualsiasi della pipeline filtro. Quando il runtime si prepara per richiamare il filtro, cerca di eseguirne il cast a un oggetto IFilterFactory. Se l'esecuzione del cast ha esito positivo, viene chiamato il metodo CreateInstance per creare l'istanza di IFilterMetadata richiamata. In questo modo viene specificata una struttura flessibile, poiché non è necessario impostare in modo esplicito la pipeline filtro all'avvio dell'app.

IFilterFactory.IsReusable:

  • Suggerimento della factory che l'istanza del filtro creata dalla factory può essere riutilizzata all'esterno dell'ambito della richiesta in cui è stata creata.
  • Non deve essere usato con un filtro che dipende dai servizi con una durata diversa da singleton.

Il runtime di ASP.NET Core non garantisce:

  • Che venga creata una singola istanza del filtro.
  • Che il filtro non verrà richiesto di nuovo dal contenitore di inserimento delle dipendenze in un momento successivo.

Avviso

Configurare per restituire la restituzione IFilterFactory.IsReusabletrue solo se l'origine dei filtri non è ambigua, i filtri sono senza stato e i filtri sono sicuri da usare tra più richieste HTTP. Ad esempio, non restituire filtri dall'inserimento di dipendenze registrati come ambito o temporanei se IFilterFactory.IsReusable restituisce true. Un altro approccio alla creazione di filtri consiste nell'implementare IFilterFactory usando implementazioni dell'attributo personalizzate:

public class AddHeaderWithFactoryAttribute : Attribute, IFilterFactory
{
    // Implement IFilterFactory
    public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
    {
        return new InternalAddHeaderFilter();
    }

    private class InternalAddHeaderFilter : IResultFilter
    {
        public void OnResultExecuting(ResultExecutingContext context)
        {
            context.HttpContext.Response.Headers.Add(
                "Internal", new string[] { "My header" });
        }

        public void OnResultExecuted(ResultExecutedContext context)
        {
        }
    }

    public bool IsReusable
    {
        get
        {
            return false;
        }
    }
}

Il filtro viene applicato nel codice seguente:

[AddHeader("Author", "Rick Anderson")]
public class SampleController : Controller
{
    public IActionResult Index()
    {
        return Content("Examine the headers using the F12 developer tools.");
    }

    [ServiceFilter(typeof(MyActionFilterAttribute))]
    public IActionResult Index2()
    {
        return Content("Header values by configuration.");
    }

    [ShortCircuitingResourceFilter]
    public IActionResult SomeResource()
    {
        return Content("Successful access to resource - header is set.");
    }

    [AddHeaderWithFactory]
    public IActionResult HeaderWithFactory()
    {
        return Content("Examine the headers using the F12 developer tools.");
    }
}

Testare il codice precedente eseguendo l'esempio di download:

  • Richiamare gli strumenti di sviluppo F12.
  • Accedere a https://localhost:5001/Sample/HeaderWithFactory.

Gli strumenti di sviluppo F12 visualizzano le intestazioni di risposta seguenti aggiunte dal codice di esempio:

  • author:Rick Anderson
  • globaladdheader:Result filter added to MvcOptions.Filters
  • internal:My header

Il codice precedente crea l'intestazione di risposta internal:My header.

Implementazione di IFilterFactory in un attributo

I filtri che implementano IFilterFactory sono utili per i filtri che:

  • Non richiedono il passaggio di parametri.
  • Hanno dipendenze del costruttore che devono essere soddisfatte dall'inserimento delle dipendenze.

TypeFilterAttribute implementa IFilterFactory. IFilterFactory espone il metodo CreateInstance per creare un'istanza IFilterMetadata. CreateInstance carica il tipo specificato dal contenitore di inserimento delle dipendenze.

public class SampleActionFilterAttribute : TypeFilterAttribute
{
    public SampleActionFilterAttribute()
                         :base(typeof(SampleActionFilterImpl))
    { 
    }

    private class SampleActionFilterImpl : IActionFilter
    {
        private readonly ILogger _logger;
        public SampleActionFilterImpl(ILoggerFactory loggerFactory)
        {
            _logger = loggerFactory.CreateLogger<SampleActionFilterAttribute>();
        }

        public void OnActionExecuting(ActionExecutingContext context)
        {
           _logger.LogInformation("SampleActionFilterAttribute.OnActionExecuting");
        }

        public void OnActionExecuted(ActionExecutedContext context)
        {
            _logger.LogInformation("SampleActionFilterAttribute.OnActionExecuted");
        }
    }
}

Il codice seguente illustra tre approcci all'applicazione di [SampleActionFilter]:

[SampleActionFilter]
public IActionResult FilterTest()
{
    return Content("From FilterTest");
}

[TypeFilter(typeof(SampleActionFilterAttribute))]
public IActionResult TypeFilterTest()
{
    return Content("From TypeFilterTest");
}

// ServiceFilter must be registered in ConfigureServices or
// System.InvalidOperationException: No service for type '<filter>'
// has been registered. Is thrown.
[ServiceFilter(typeof(SampleActionFilterAttribute))]
public IActionResult ServiceFilterTest()
{
    return Content("From ServiceFilterTest");
}

Nel codice precedente la decorazione del metodo con [SampleActionFilter] è l'approccio da preferire per applicare SampleActionFilter.

Uso di middleware nella pipeline filtro

I filtri risorse funzionano come middleware in quanto racchiudono l'esecuzione di tutto ciò che viene dopo nella pipeline. Tuttavia, i filtri differiscono dal middleware in quanto fanno parte del runtime, il che significa che hanno accesso al contesto e ai costrutti.

Per usare il middleware come filtro, creare un tipo con un metodo Configure che specifica il middleware da inserire nella pipeline di filtro. L'esempio seguente usa il middleware di localizzazione per stabilire le impostazioni cultura correnti per una richiesta:

public class LocalizationPipeline
{
    public void Configure(IApplicationBuilder applicationBuilder)
    {
        var supportedCultures = new[]
        {
            new CultureInfo("en-US"),
            new CultureInfo("fr")
        };

        var options = new RequestLocalizationOptions
        {
            DefaultRequestCulture = new RequestCulture(
                                       culture: "en-US", 
                                       uiCulture: "en-US"),
            SupportedCultures = supportedCultures,
            SupportedUICultures = supportedCultures
        };
        options.RequestCultureProviders = new[] 
            { new RouteDataRequestCultureProvider() {
                Options = options } };

        applicationBuilder.UseRequestLocalization(options);
    }
}

Usare MiddlewareFilterAttribute per eseguire il middleware:

[Route("{culture}/[controller]/[action]")]
[MiddlewareFilter(typeof(LocalizationPipeline))]
public IActionResult CultureFromRouteData()
{
    return Content(
          $"CurrentCulture:{CultureInfo.CurrentCulture.Name},"
        + $"CurrentUICulture:{CultureInfo.CurrentUICulture.Name}");
}

I filtri middleware vengono eseguiti nella stessa fase della pipeline filtro come filtri risorse, prima dell'associazione di modelli e dopo il resto della pipeline.

Thread safety

Quando si passa un'istanza di un filtro in Add, anziché il relativo Type, il filtro è un singleton e non è thread-safe.

Azioni successive