Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
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, interrompendo il flusso delle richieste 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:
Tipi di filtri
Ogni tipo di filtro viene eseguito in una fase diversa della pipeline di filtro:
-
- Corri prima.
- Determinare se l'utente è autorizzato per la richiesta.
- Interrompi il flusso della pipeline se la richiesta non è autorizzata.
-
- Esegui dopo l'autorizzazione.
-
OnResourceExecuting esegue il codice prima del resto della pipeline di filtraggio. Ad esempio,
OnResourceExecuting
esegue il codice prima dell'associazione di modelli. - OnResourceExecuted esegue il codice software dopo che il resto della pipeline è stato completato.
-
- Eseguito immediatamente prima e dopo l'invocazione 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.
-
- Eseguito immediatamente prima e dopo che viene chiamato un metodo d'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 delle eccezioni applicano criteri globali alle eccezioni non gestite che si verificano prima che il corpo della risposta sia stato scritto.
-
- 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:
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 sia prima che dopo la loro fase nella 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 ActionExecutionDelegate, next
, che esegue il metodo dell'azione.
Fasi di filtro multiple
È possibile implementare interfacce per più fasi di filtro in una singola classe. Ad esempio, la ActionFilterAttribute classe implementa:
- Sincrono: IActionFilter e IResultFilter
- Asincrono: IAsyncActionFilter e IAsyncResultFilter
- IOrderedFilter
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. Nelle Intestazioni di risposta, filter-header: Filter Value
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:
- ActionFilterAttribute
- ExceptionFilterAttribute
- ResultFilterAttribute
- FormatFilterAttribute
- ServiceFilterAttribute
- TypeFilterAttribute
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 le Razor pagine, come mostrato 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 before 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:
Sequenza | Ambito del filtro | Metodo di filtraggio |
---|---|---|
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 metodi OnActionExecuting, OnActionExecutionAsync e OnActionExecuted. Questi metodi incapsulano i filtri eseguiti per una determinata azione.
-
OnActionExecuting
viene eseguito prima di tutti i filtri dell'azione. -
OnActionExecuted
viene eseguito dopo tutti i filtri dell'azione. -
OnActionExecutionAsync
viene eseguito prima di qualsiasi filtro dell'azione. Il codice dopo una chiamata anext
viene eseguito dopo i filtri dell'azione.
La classe seguente ControllerFiltersController
:
- Applica l'oggetto
SampleActionFilterAttribute
([SampleActionFilter]
) al controller. - Esegue l'override di
OnActionExecuting
eOnActionExecuted
.
[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 successivamente a quello di un filtro con un valore
Order
più alto.
Nell'esempio di filtri a livello di controller, GlobalSampleActionFilter
ha un ambito globale, quindi viene eseguito prima di SampleActionFilterAttribute
, che ha ambito controller. Per eseguire SampleActionFilterAttribute
come primo, imposta il suo ordine a 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 cortocircuito
È 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. - Cortocircuita 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.
ShortCircuitingResourceFilterAttribute
viene eseguito per primo a causa del suo tipo di filtro.
[ResponseHeader("Filter-Header", "Filter Value")]
public class ShortCircuitingController : Controller
{
[ShortCircuitingResourceFilter]
public IActionResult Index() =>
Content($"- {nameof(ShortCircuitingController)}.{nameof(Index)}");
}
Iniezione 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, esso viene attivato. Un filtro attivato in base al tipo significa:
- Viene creata un'istanza per ogni richiesta.
- Le dipendenze del costruttore sono popolate tramite iniezione delle dipendenze (DI).
I filtri implementati come attributi e aggiunti direttamente alle classi controller o ai metodi di azione non possono avere dipendenze del 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 seguenti filtri supportano le dipendenze del costruttore fornite dal dependency injection:
- ServiceFilterAttribute
- TypeFilterAttribute
- IFilterFactory implementato nell'attributo.
I filtri precedenti possono essere applicati a un controller o a un'azione.
I logger sono disponibili tramite l'Iniezione di 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. È stato aggiunto il registro ai filtri:
- Deve concentrarsi su aspetti del dominio aziendale o comportamenti specifici del filtro.
- Non dovrebbe 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 mostra la classe LoggingResponseHeaderFilterService
, che utilizza l'iniezione 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
e si imposta 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.
- Il filtro non verrà richiesto di nuovo dal contenitore DI in un momento successivo.
- Non dovrebbe essere utilizzato con un filtro che si basa su servizi con un ciclo di vita diverso 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:
- Non è necessario registrare nel contenitore DI i tipi a cui viene fatto riferimento tramite
TypeFilterAttribute
. 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.
- Controlla 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:
- Implementano l'interfaccia IResourceFilter o IAsyncResourceFilter.
- L'esecuzione avvolge la maggior parte della pipeline di filtro.
- Solo i filtri di autorizzazione vengono eseguiti prima dei filtri di risorse.
I filtri di risorse sono utili per aggirare gran parte della pipeline. Ad esempio, un filtro di memorizzazione nella cache può evitare il resto della pipeline nel caso di un hit della cache.
Esempi di filtri di risorse:
Il filtro di risorse di corto circuito illustrato in precedenza.
DisableFormValueModelBindingAttribute:
- Impedisce all'associazione di modelli di accedere ai dati del modulo.
- Viene usato quando si caricano file di grandi dimensioni per impedire la lettura dei dati del modulo in memoria.
Filtri di azione
I filtri di 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:
- Implementano l'interfaccia IActionFilter o IAsyncActionFilter.
- La loro esecuzione accompagna l'esecuzione dei metodi 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.
Generare 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 l'esecuzione dell'azione è stata interrotta da un altro filtro.
-
Exception - Non nullo 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 d'azione successivo e il metodo d'azione.
- Restituisce
ActionExecutedContext
.
Per eseguire un cortocircuito, assegnare Microsoft.AspNetCore.Mvc.Filters.ActionExecutingContext.Result a un'istanza di 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 l'interruzione dell'esecuzione dell'azione.
- L'impostazione di Exception è un valore diverso da nullo se l'azione o un filtro 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.
Filtri eccezioni
I filtri delle eccezioni:
- Implementa IExceptionFilter o IAsyncExceptionFilter.
- Possono essere usati per implementare criteri di gestione degli errori comuni.
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 per eccezioni
- Non ci sono eventi prima o dopo.
- Implementa OnException o OnExceptionAsync.
- Gestire le eccezioni non gestite che si verificano nella creazione di pagine o controller,Razor nell'associazione di modelli, nei filtri di azione o nei metodi di azione.
- Non intercettare 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 per 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 dell'API possono restituire informazioni sull'errore come JSON, mentre le azioni basate sulla visualizzazione possono restituire una pagina di errore in formato HTML.
I filtri del risultato
I filtri dei risultati:
- Implementare un'interfaccia:
- La loro esecuzione riguarda l'attuazione dei risultati delle azioni.
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 di 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 gestisce un'eccezione producendo un risultato di un'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. Lancio 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.
ResultExecutedContext.Canceled
è impostato su true
se l'esecuzione del risultato dell'azione è stata interrotta da un altro filtro.
ResultExecutedContext.Exception
è impostato su un valore non nullo 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 creare un corto circuito, impostare ResultExecutingContext.Cancel su true
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 delle 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. Questo fornisce un design flessibile, poiché non è necessario impostare in modo esplicito la pipeline di filtro all'avvio dell'app.
IFilterFactory.IsReusable
:
- È un suggerimento dal factory che l'istanza del filtro creata dal factory può essere riutilizzata al di fuori dell'ambito di richiesta in cui è stata creata.
- Non dovrebbe essere utilizzato con un filtro che dipende da servizi con una durata diversa da singleton.
Il runtime di ASP.NET Core non garantisce:
- Che venga creata una singola istanza del filtro.
- Il filtro non verrà richiesto nuovamente dal contenitore delle dipendenze in un momento successivo.
Avviso
Configurare IFilterFactory.IsReusable per restituire true
solo se l'origine dei filtri è chiara, i filtri sono senza stato e sono sicuri da usare in diverse 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.
- Le dipendenze del costruttore devono essere soddisfatte dal DI.
TypeFilterAttribute implementa IFilterFactory.
IFilterFactory
espone il metodo CreateInstance per creare un'istanza IFilterMetadata.
CreateInstance
carica il tipo specificato dal contenitore DI.
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 dei filtri delle risorse, prima dell'associazione al modello e dopo il resto della pipeline.
Sicurezza dei thread
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:
Tipi di filtri
Ogni tipo di filtro viene eseguito in una fase diversa della pipeline di filtro:
-
- Corri prima.
- Determinare se l'utente è autorizzato per la richiesta.
- Interrompi la pipeline se la richiesta non è autorizzata.
-
- Esegui dopo l'autorizzazione.
-
OnResourceExecuting esegue il codice prima di tutto il resto della pipeline di filtri. Ad esempio,
OnResourceExecuting
esegue il codice prima dell'associazione di modelli. - OnResourceExecuted esegue il codice software dopo che il resto della pipeline è stato completato.
-
- 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.
-
- 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 sia sulle azioni che sugli endpoint basati su gestori di route.
I filtri per eccezioni applicano criteri globali a eccezioni non gestite, che si verificano prima che il corpo della risposta sia stato scritto.
-
- 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 la visualizzazione o l'esecuzione del formattatore.
Il diagramma seguente mostra come interagiscono i tipi di filtro nella pipeline di filtro:
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, il SampleAsyncActionFilter
ha un ActionExecutionDelegate e un 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:
- Sincrono: IActionFilter e IResultFilter
- Asincrono: IAsyncActionFilter e IAsyncResultFilter
- IOrderedFilter
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. Sotto Intestazioni di risposta, filter-header: Filter Value
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:
- ActionFilterAttribute
- ExceptionFilterAttribute
- ResultFilterAttribute
- FormatFilterAttribute
- ServiceFilterAttribute
- TypeFilterAttribute
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 su un controller o su una Razor 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 mostrato 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 nell'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 before 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:
Sequenza | Ambito del filtro | Metodo 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 OnActionExecuting metodi, OnActionExecutionAsync e OnActionExecuted. Questi metodi incapsulano i filtri eseguiti per una determinata azione:
-
OnActionExecuting
viene eseguito prima di qualsiasi filtro dell'azione. -
OnActionExecuted
viene eseguito dopo tutti i filtri relativi all'azione. -
OnActionExecutionAsync
viene eseguito prima di qualsiasi filtro dell'azione. Il codice dopo una chiamata anext
viene eseguito dopo i filtri dell'azione.
La classe seguente ControllerFiltersController
:
- Applica l'oggetto
SampleActionFilterAttribute
([SampleActionFilter]
) al controller. - Esegue l'override di
OnActionExecuting
eOnActionExecuted
.
[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
Order
più alto.
Nell'esempio dei filtri a livello di controller, GlobalSampleActionFilter
ha un ambito globale, quindi viene eseguito prima di SampleActionFilterAttribute
, che ha un ambito controller. Per dare la precedenza a SampleActionFilterAttribute
, 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 cortocircuitazione
È 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. - Cortocircuita 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. Il ShortCircuitingResourceFilterAttribute
viene eseguito per primo a causa del suo tipo di filtro.
[ResponseHeader("Filter-Header", "Filter Value")]
public class ShortCircuitingController : Controller
{
[ShortCircuitingResourceFilter]
public IActionResult Index() =>
Content($"- {nameof(ShortCircuitingController)}.{nameof(Index)}");
}
Iniezione 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, questo viene attivato. Un filtro attivato dal tipo comporta:
- Viene creata un'istanza per ogni richiesta.
- Qualsiasi dipendenza del costruttore viene popolata tramite inserimento delle dipendenze (DI).
I filtri implementati come attributi e aggiunti direttamente alle classi controller o ai metodi di azione non possono avere dipendenze del costruttore specificate dall'iniezione delle dipendenze (DI). 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:
- ServiceFilterAttribute
- TypeFilterAttribute
- IFilterFactory implementato nell'attributo.
I filtri precedenti possono essere applicati a un controller o a un'azione.
I logger sono disponibili tramite DI. 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. Il log è stato aggiunto ai filtri:
- Deve concentrarsi su problemi del dominio di business o comportamenti specifici del filtro.
- Non dovrebbe 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'Inversione di Controllo.
Il codice seguente illustra la classe LoggingResponseHeaderFilterService
, che utilizza l'iniezione 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:
builder.Services.AddScoped<LoggingResponseHeaderFilterService>();
Nel codice seguente l'attributo ServiceFilter
recupera un'istanza del filtro LoggingResponseHeaderFilterService
da Dependency Injection:
[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.
- Il filtro non verrà richiesto nuovamente dal contenitore di iniezione delle dipendenze in un momento successivo.
- Non deve essere usato con un filtro che dipende da servizi che hanno una durata di vita diversa da quella del 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 DI. Viene creata un'istanza del tipo tramite Microsoft.Extensions.DependencyInjection.ObjectFactory.
Poiché i tipi TypeFilterAttribute
non vengono risolti direttamente dal contenitore DI:
- Non è necessario che i tipi riferiti tramite
TypeFilterAttribute
siano registrati nel contenitore DI. Le loro dipendenze vengono soddisfatte dal contenitore DI. - 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.
- Controlla 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:
- Implementano l'interfaccia IResourceFilter o IAsyncResourceFilter.
- L'esecuzione racchiude la maggior parte della pipeline di filtro.
- Solo i filtri di autorizzazione vengono eseguiti prima dei filtri di risorse.
I filtri di risorse sono utili per interrompere la maggior parte del flusso della pipeline. Ad esempio, un filtro di memorizzazione nella cache può evitare il resto della pipeline nel caso di un hit della cache.
Esempi di filtri di risorse:
Il filtro di risorse di corto circuito illustrato in precedenza.
DisableFormValueModelBindingAttribute:
- Impedisce all'associazione di modelli di accedere ai dati del modulo.
- Viene usato quando si caricano file di grandi dimensioni per impedire la lettura dei dati del modulo in memoria.
Filtri di azione
I filtri di azione non si applicano alle Razor Pagine. Razor Pages supportano IPageFilter e IAsyncPageFilter. Per altre informazioni, vedere Metodi di filtro per Razor Pagine.
I filtri di azione:
- Implementano l'interfaccia IActionFilter o IAsyncActionFilter.
- La loro esecuzione si accompagna all'esecuzione dei metodi 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 l'esecuzione dell'azione è stata interrotta da un altro filtro.
-
Exception - Non nullo se l'azione o un filtro d'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 eventuali filtri d'azione successivi e il metodo d'azione.
- Restituisce
ActionExecutedContext
.
Per cortocircuitare, assegnare Microsoft.AspNetCore.Mvc.Filters.ActionExecutingContext.Result a un'istanza di 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.
- Restituisci 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 l'esecuzione dell'azione è stata interrotta da un altro filtro.
- L'impostazione di Exception è un valore non Null se l'azione o un filtro di azione successivo ha generato un'eccezione. Imposta
Exception
a 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:
- Implementa IExceptionFilter o IAsyncExceptionFilter.
- Possono essere usati per implementare criteri di gestione degli errori comuni.
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 ci sono eventi precedenti o successivi.
- Implementa OnException o OnExceptionAsync.
- Gestire le eccezioni non gestite che si verificano nella creazione di pagine o controller,Razor nell'associazione di modelli, nei filtri di azione o nei metodi di azione.
- Non intercettare 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 dell'API possono restituire informazioni sull'errore come JSON, mentre le azioni basate sulla visualizzazione possono restituire una pagina di errore in formato HTML.
Filtri di risultato
I filtri dei risultati:
- Implementare un'interfaccia:
- La loro esecuzione coinvolge l'esecuzione dei risultati dell'azione.
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 di 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 gestisce un'eccezione producendo un risultato d'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. Lancio 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.
ResultExecutedContext.Canceled
è impostato su true
se l'esecuzione del risultato dell'azione è stata interrotta da un altro filtro.
ResultExecutedContext.Exception
è impostato su un valore diverso da 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 causare un cortocircuito, impostare ResultExecutingContext.Cancel su true
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 effettuarne 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. Questo offre un design flessibile, poiché non è necessario impostare in modo esplicito la pipeline dei filtri all'avvio dell'app.
IFilterFactory.IsReusable
:
- È un suggerimento della fabbrica che l'istanza del filtro creata può essere riutilizzata al di fuori dell'ambito della richiesta in cui è stata creata.
- Non dovrebbe 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.
- Il filtro non verrà richiesto nuovamente dal contenitore DI in un momento successivo.
Avviso
Configurare IFilterFactory.IsReusable per restituire true
solo se l'origine dei filtri non è ambigua, i filtri sono senza stato e i filtri sono sicuri da usare su 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 dei servizi.
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.
Utilizzare il middleware nella pipeline di filtro
I filtri delle risorse funzionano come middleware poiché racchiudono l'esecuzione di tutto ciò che segue 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 dei filtri delle risorse, prima dell'associazione al modello e dopo il resto della pipeline.
Sicurezza dei thread
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, interrompendo la pipeline di richiesta per restituire una risposta già 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.
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.
-
- Esegui dopo l'autorizzazione.
-
OnResourceExecuting esegue il codice prima del resto della pipeline di filtri. Ad esempio,
OnResourceExecuting
esegue il codice prima dell'associazione di modelli. - OnResourceExecuted esegue il codice software dopo che il resto della pipeline è stato completato.
-
- 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 delle eccezioni applicano criteri globali a eccezioni non gestite che si verificano prima che il corpo della risposta sia stato scritto.
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 circondare l'esecuzione di una vista o di un formattatore.
Il diagramma seguente illustra come interagiscono i tipi di filtro nella pipeline di filtro.
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:
- Sincrono: IActionFilter e IResultFilter
- Asincrono: IAsyncActionFilter e IAsyncResultFilter
- IOrderedFilter
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 per sviluppatori del browser per esaminare le intestazioni. In Intestazioni di risposta, author: Rick Anderson
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": "*"
}
Nel StartUp.ConfigureServices
:
- La
PositionOptions
classe viene aggiunta al contenitore del servizio con l'area"Position"
di configurazione. - L'oggetto
MyActionFilterAttribute
viene aggiunto al container 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.");
}
Sotto Intestazioni di risposta, author: Rick Anderson
e Editor: Joe Smith
viene visualizzato quando viene chiamato l'endpoint Sample/Index2
.
Il codice seguente applica MyActionFilterAttribute
e AddHeaderAttribute
alla pagina Razor.
[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:
- ActionFilterAttribute
- ExceptionFilterAttribute
- ResultFilterAttribute
- FormatFilterAttribute
- ServiceFilterAttribute
- TypeFilterAttribute
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 nell'ordine opposto 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 post dei filtri del controller e della pagina.
- Codice precedente 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.
Sequenza | Ambito del filtro | Metodo filtro |
---|---|---|
1 | Generale | OnActionExecuting |
2 | Controller o Razor Pagina | OnActionExecuting |
3 | metodo | OnActionExecuting |
4 | metodo | 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.OnActionExecuting, Controller.OnActionExecutionAsync, e Controller.OnActionExecutedOnActionExecuted
metodi. Questi metodi:
- Avvolgere i filtri che vengono eseguiti per un'azione specifica.
- La chiamata di
OnActionExecuting
avviene prima di qualsiasi filtro dell'azione. - La chiamata a
OnActionExecuted
avviene dopo tutti i filtri di azione. - La chiamata di
OnActionExecutionAsync
avviene prima di qualsiasi filtro dell'azione. Il codice del filtro doponext
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'azioneFilterTest2
. - Sostituisce
OnActionExecuting
eOnActionExecuted
.
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.
Per sostituire l'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
Order
più alto.
La Order
proprietà viene impostata con un parametro del costruttore:
[SampleActionFilter(Order = int.MinValue)]
Considerare i due filtri di 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, quindi viene eseguito prima di MyAction2FilterAttribute
, che ha un ambito di controller. Per eseguire MyAction2FilterAttribute
innanzitutto, 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 far eseguire prima il filtro globale MySampleActionFilter
, impostare Order
su int.MinValue
:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews(options =>
{
options.Filters.Add(typeof(MySampleActionFilter),
int.MinValue);
});
}
Annullamento e cortocircuitazione
È 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 = "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. - Cortocircuita 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.");
}
}
Iniezione 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, viene attivato il tipo. Un filtro attivato in base al tipo significa:
- Viene creata un'istanza per ogni richiesta.
- Qualsiasi dipendenza del costruttore viene popolata tramite iniezione delle dipendenze (DI).
I filtri implementati come attributi e aggiunti direttamente alle classi controller o ai metodi di azione non possono avere dipendenze del costruttore specificate dall'iniezione 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:
- ServiceFilterAttribute
- TypeFilterAttribute
- IFilterFactory implementato nell'attributo.
I filtri precedenti possono essere applicati a un controller o a un metodo di azione:
I logger sono disponibili tramite Dependency Injection. Evitare tuttavia di creare e usare filtri esclusivamente per scopi di registrazione. La funzionalità di registrazione del framework predefinita solitamente fornisce tutto il necessario per la registrazione. Registrazione aggiunta ai filtri:
- Deve concentrarsi sui problemi del dominio aziendale o sugli aspetti o comportamenti specifici del filtro.
- Non deve registrare azioni o altri eventi del framework. I filtri integrati registrano le azioni 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.
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.
- Il filtro non verrà richiesto nuovamente dal contenitore DI 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. Istanzia il tipo utilizzando Microsoft.Extensions.DependencyInjection.ObjectFactory.
Poiché i tipi TypeFilterAttribute
non vengono risolti direttamente dal contenitore DI:
- Non è necessario che i tipi a cui viene fatto riferimento tramite
TypeFilterAttribute
siano registrati nel contenitore DI. Le loro dipendenze vengono soddisfatte dal contenitore DI. - 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 dei filtri.
- Controllare 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 lanciare 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:
- Implementano l'interfaccia IResourceFilter o IAsyncResourceFilter.
- L'esecuzione racchiude la maggior parte della pipeline di filtro.
- Solo i filtri di autorizzazione vengono eseguiti prima dei filtri di risorse.
I filtri di risorse sono utili per interrompere la maggior parte della pipeline. Ad esempio, un filtro di memorizzazione nella cache può evitare il resto della pipeline nel caso di un hit della cache.
Esempi di filtri di risorse:
Il filtro di risorse di corto circuito illustrato in precedenza.
DisableFormValueModelBindingAttribute:
- Impedisce all'associazione di modelli di accedere ai dati del modulo.
- Viene usato quando si caricano file di grandi dimensioni per impedire la lettura dei dati del modulo in memoria.
Filtri azione
I filtri di 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:
- Implementano l'interfaccia IActionFilter o IAsyncActionFilter.
- La loro esecuzione circonda l'esecuzione dei metodi 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 per 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 sollevazione di un'eccezione in un metodo d'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 l'esecuzione dell'azione è stata interrotta da un altro filtro.
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 eventuali filtri di azioni successivi 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
(il 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.
- Restituisci 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 interrotto l'esecuzione dell'azione.
Il valore di Exception è impostato a un valore non nullo se l'azione o un filtro d'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 per eccezioni
Filtri eccezioni:
- Implementa IExceptionFilter o IAsyncExceptionFilter.
- Possono essere usati per implementare criteri di gestione degli errori comuni.
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 delle eccezioni
- Non ci sono eventi precedenti o successivi.
- Implementa OnException o OnExceptionAsync.
- Gestire le eccezioni non gestite che si verificano nella creazione di pagine o controller,Razor nell'associazione di modelli, nei filtri di azione o nei metodi di azione.
- Non catturare le eccezioni che si verificano nei filtri delle 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 dell'API possono restituire informazioni sull'errore come JSON, mentre le azioni basate sulla visualizzazione possono restituire una pagina di errore in formato HTML.
Filtri dei risultati
I filtri dei risultati:
- Implementare un'interfaccia:
- La loro esecuzione coinvolge il processo che porta ai risultati delle azioni.
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 di 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 gestisce un'eccezione producendo un risultato di un'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. Lancio 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.
ResultExecutedContext.Canceled
viene impostato su true
se un altro filtro ha interrotto l'esecuzione del risultato dell'azione.
ResultExecutedContext.Exception
viene impostato su un valore non nullo se il risultato dell'azione o un filtro di 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 creare un cortocircuito, impostare ResultExecutingContext.Cancel su true
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 di eccezione.
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 a invocare il filtro, cerca di eseguirne il casting a un oggetto IFilterFactory
. Se l'esecuzione del cast ha esito positivo, viene chiamato il metodo CreateInstance per creare l'istanza di IFilterMetadata
richiamata. Questo fornisce un design flessibile, poiché non è necessario impostare esplicitamente la pipeline del filtro quando l'app viene avviata.
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 dovrebbe essere usato con un filtro che dipende da servizi con un ciclo di vita diverso da singleton.
Il runtime di ASP.NET Core non garantisce:
- Che venga creata una singola istanza del filtro.
- Il filtro non verrà richiesto di nuovo dal contenitore DI in un momento successivo.
Avviso
Configura IFilterFactory.IsReusable per restituire true
solo se l'origine dei filtri è non ambigua, i filtri sono senza stato e sono sicuri da usare attraverso diverse 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 scaricabile:
- Richiama gli strumenti di sviluppo F12.
- Vai a
https://localhost:5001/Sample/HeaderWithFactory
.
Gli strumenti di sviluppo F12 visualizzano le intestazioni di risposta seguenti aggiunte dal codice di esempio:
-
autore:
Rick Anderson
-
globaladdheader:
Result filter added to MvcOptions.Filters
-
interno:
My header
Il codice precedente crea l'intestazione della risposta interna: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 tramite Dependency Injection.
TypeFilterAttribute implementa IFilterFactory.
IFilterFactory
espone il metodo CreateInstance per creare un'istanza IFilterMetadata.
CreateInstance
carica il tipo specificato dal contenitore dei servizi (DI).
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 del middleware nella pipeline dei filtri
I filtri delle risorse funzionano come middleware in quanto racchiudono l'esecuzione di tutto ciò che segue 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 la cultura attuale 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 dei filtri delle risorse, prima dell'associazione al modello e dopo il resto della pipeline.
Sicurezza dei thread
Quando si passa un'istanza di un filtro a Add
, anziché il relativo Type
, il filtro è un singleton e non è thread-safe.
Azioni successive
- Vedere Metodi di filtro per Razor Pages.
- Per sperimentare i filtri, scaricare, testare e modificare l'esempio di GitHub.