Filtry v ASP.NET Core
Kirk Larkin, Rick Anderson, Tom Dykstra a Steve Smith
Filtry v ASP.NET Core umožňují spuštění kódu před nebo po konkrétních fázích v kanálu zpracování požadavků.
Předdefinované filtry zpracovávají úlohy, jako jsou:
- Autorizace bránící přístupu k prostředkům, pro které uživatel nemá oprávnění.
- Ukládání odpovědí do mezipaměti, zkratování kanálu požadavku za účelem vrácení odpovědi uložené v mezipaměti
Vlastní filtry je možné vytvořit pro zpracování problémů s křížovým dělením. Mezi průřezové aspekty patří zpracování chyb, ukládání do mezipaměti, konfigurace, autorizace a protokolování. Filtry se vyhýbají duplikování kódu. Filtr výjimek zpracování chyb může například konsolidovat zpracování chyb.
Tento dokument se vztahuje na Razor stránky, kontrolery rozhraní API a kontrolery se zobrazeními. Filtry nefungují přímo se součástmiRazor. Filtr může pouze nepřímo ovlivnit komponentu v případech, kdy:
- Komponenta je vložena do stránky nebo zobrazení.
- Stránka nebo kontroler a zobrazení používají filtr.
Jak fungují filtry
Filtry se spouští v kanálu vyvolání akce ASP.NET Core, někdy označovaného jako kanál filtru. Kanál filtru se spustí po ASP.NET Core vybere akci, která se má provést:
Typy filtrů
Každý typ filtru se provádí v jiné fázi v kanálu filtru:
Filtry autorizace:
- Nejprve spusťte.
- Určete, jestli má uživatel oprávnění k žádosti.
- Zkratovat kanál, pokud požadavek není autorizovaný.
Filtry prostředků:
- Spusťte po autorizaci.
- OnResourceExecuting spustí kód před zbývající částí kanálu filtru. Například
OnResourceExecuting
spustí kód před vazbou modelu. - OnResourceExecuted spustí kód po dokončení zbývající části kanálu.
Filtry akcí:
- Spusťte bezprostředně před a po zavolání metody akce.
- Může změnit argumenty předané do akce.
- Může změnit výsledek vrácený z akce.
- Stránky se nepodporujíRazor.
Filtry koncových bodů:
- Spusťte bezprostředně před a po zavolání metody akce.
- Může změnit argumenty předané do akce.
- Může změnit výsledek vrácený z akce.
- Stránky se nepodporujíRazor.
- Je možné vyvolat jak u akcí, tak u koncových bodů založených na obslužné rutině směrování.
Filtry výjimek aplikují globální zásady na neošetřené výjimky, ke kterým dochází před zápisem textu odpovědi.
Filtry výsledků:
- Spusťte bezprostředně před a po provedení výsledků akce.
- Spusťte pouze v případech, kdy se metoda akce úspěšně provede.
- Jsou užitečné pro logiku, která musí obklopovat provádění zobrazení nebo formátovače.
Následující diagram znázorňuje interakci typů filtrů v kanálu filtru:
Razor Stránky také podporují Razor filtry stránek, které se spouští před a za obslužnou rutinou Razor stránky.
Implementace
Filtry podporují synchronní i asynchronní implementace prostřednictvím různých definic rozhraní.
Synchronní filtry se spouštějí před a po fázi kanálu. Volá se například OnActionExecuting před zavolání metody akce. OnActionExecuted je volána po vrácení metody akce:
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.
}
}
Asynchronní filtry definují metodu On-Stage-ExecutionAsync
. Příklad: 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.
}
}
V předchozím kódu má SampleAsyncActionFilter
znak ActionExecutionDelegate, next
který provede metodu akce.
Více fází filtru
Rozhraní pro více fází filtru lze implementovat v jedné třídě. Například ActionFilterAttribute třída implementuje:
- Synchronní: IActionFilter a IResultFilter
- Asynchronní: IAsyncActionFilter a IAsyncResultFilter
- IOrderedFilter
Implementujte synchronní nebo asynchronní verzi rozhraní filtru, nikoli obě. Modul runtime nejprve zkontroluje, jestli filtr implementuje asynchronní rozhraní, a pokud ano, zavolá to. Pokud ne, volá synchronní metody rozhraní. Pokud jsou asynchronní i synchronní rozhraní implementována v jedné třídě, volá se pouze asynchronní metoda. Při použití abstraktních tříd, jako ActionFilterAttributeje , přepsat pouze synchronní metody nebo asynchronní metody pro každý typ filtru.
Předdefinované atributy filtru
ASP.NET Core obsahuje integrované filtry založené na atributech, které je možné přizpůsobit a podtřídy. Například následující filtr výsledků přidá hlavičku do odpovědi:
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);
}
}
Atributy umožňují filtrům přijímat argumenty, jak je znázorněno v předchozím příkladu. ResponseHeaderAttribute
Použijte metodu kontroleru nebo akce a zadejte název a hodnotu hlavičky HTTP:
[ResponseHeader("Filter-Header", "Filter Value")]
public class ResponseHeaderController : ControllerBase
{
public IActionResult Index() =>
Content("Examine the response headers using the F12 developer tools.");
// ...
K prozkoumání hlaviček použijte nástroj, například vývojářské nástroje prohlížeče. V části Hlavičkyfilter-header: Filter Value
odpovědi se zobrazí.
Následující kód platí ResponseHeaderAttribute
pro kontroler i akci:
[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.");
}
Odpovědi z Multiple
akce zahrnují následující hlavičky:
filter-header: Filter Value
another-filter-header: Another Filter Value
Několik rozhraní filtru má odpovídající atributy, které lze použít jako základní třídy pro vlastní implementace.
Atributy filtru:
- ActionFilterAttribute
- ExceptionFilterAttribute
- ResultFilterAttribute
- FormatFilterAttribute
- ServiceFilterAttribute
- TypeFilterAttribute
Filtry nelze použít u Razor metod obslužné rutiny stránky. Dají se použít buď na Razor model stránky, nebo globálně.
Filtrování oborů a pořadí provádění
Do kanálu je možné přidat filtr v jednom ze tří oborů:
- Použití atributu na kontroleru nebo Razor stránce
- Použití atributu v akci kontroleru Atributy filtru nelze použít u Razor metod obslužné rutiny Pages.
- Globálně pro všechny kontrolery, akce a Razor stránky, jak je znázorněno v následujícím kódu:
var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.AddControllersWithViews(options => { options.Filters.Add<GlobalSampleActionFilter>(); });
Výchozí pořadí provádění
Pokud existuje více filtrů pro určitou fázi kanálu, rozsah určuje výchozí pořadí provádění filtru. Globální filtry obklopují filtry tříd, které následně obklopují filtry metod ohraničí.
V důsledku vnoření filtru se kód filtrů spustí v opačném pořadí před kódem. Posloupnost filtrů:
- Před kódem globálních filtrů.
- Před kódem kontroleru se filtruje.
- Před kódem metody akce filtruje.
- Následující kód metody akce filtruje.
- Kód po kontroleru filtruje.
- Před kódem kontroleru se filtruje.
- Kód po globálních filtrech
Následující příklad znázorňuje pořadí spuštění metod filtru pro synchronní filtry akcí:
Sequence | Obor filtru | Metoda filtru |
---|---|---|
0 | Globální | OnActionExecuting |
2 | Ovladač | OnActionExecuting |
3 | Akce | OnActionExecuting |
4 | Akce | OnActionExecuted |
5 | Ovladač | OnActionExecuted |
6 | Globální | OnActionExecuted |
Filtry na úrovni kontroleru
Každý kontroler, který dědí z Controller , zahrnuje OnActionExecuting, OnActionExecutionAsynca OnActionExecuted metody. Tyto metody zabalí filtry, které se spustí pro danou akci:
OnActionExecuting
spustí se před libovolným filtrem akce.OnActionExecuted
spustí se po všech filtrech akce.OnActionExecutionAsync
spustí se před libovolným filtrem akce. Kód po volání, který senext
spustí po filtrech akce.
Následující ControllerFiltersController
třída:
SampleActionFilterAttribute
Použije ([SampleActionFilter]
) na kontroler.- Přepsání
OnActionExecuting
aOnActionExecuted
.
[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.");
}
}
Navigace pro https://localhost:<port>/ControllerFilters
spuštění následujícího kódu:
ControllerFiltersController.OnActionExecuting
GlobalSampleActionFilter.OnActionExecuting
SampleActionFilterAttribute.OnActionExecuting
ControllerFiltersController.Index
SampleActionFilterAttribute.OnActionExecuted
GlobalSampleActionFilter.OnActionExecuted
ControllerFiltersController.OnActionExecuted
Filtry na úrovni kontroleru nastavily vlastnost Order na int.MinValue
hodnotu . Filtry na úrovni kontroleru nelze nastavit tak, aby se spouštěly po použití filtrů na metody. Pořadí je vysvětleno v další části.
V případě Razor stránek naleznete v tématu Implementace Razor filtrů stránky přepsáním metod filtru.
Přepsání výchozího pořadí
Výchozí posloupnost provádění lze přepsat implementací IOrderedFilter. IOrderedFilter
Order zveřejňuje vlastnost, která má přednost před oborem k určení pořadí provádění. Filtr s nižší Order
hodnotou:
- Spustí před kódem před filtrem s vyšší hodnotou
Order
. - Spustí následující kód po filtru s vyšší
Order
hodnotou.
V příkladu filtrů na úrovni kontroleru má globální obor, GlobalSampleActionFilter
aby se spustil před SampleActionFilterAttribute
, který má obor kontroleru. Pokud chcete provést SampleActionFilterAttribute
spuštění jako první, nastavte jeho pořadí na int.MinValue
:
[SampleActionFilter(Order = int.MinValue)]
public class ControllerFiltersController : Controller
{
// ...
}
Pokud chcete, aby se globální filtr GlobalSampleActionFilter
spustil jako první, nastavte jeho Order
hodnotu na int.MinValue
:
builder.Services.AddControllersWithViews(options =>
{
options.Filters.Add<GlobalSampleActionFilter>(int.MinValue);
});
Zrušení a zkratování
Kanál filtru může být zkrácený nastavením Result vlastnosti parametru ResourceExecutingContext poskytnutého metodě filtru. Například následující filtr prostředků brání spuštění zbytku kanálu:
public class ShortCircuitingResourceFilterAttribute : Attribute, IResourceFilter
{
public void OnResourceExecuting(ResourceExecutingContext context)
{
context.Result = new ContentResult
{
Content = nameof(ShortCircuitingResourceFilterAttribute)
};
}
public void OnResourceExecuted(ResourceExecutedContext context) { }
}
V následujícím kódu cílí metoda akce jak na [ShortCircuitingResourceFilter]
filtr, tak i na metodu [ResponseHeader]
Index
akce. Filtr ShortCircuitingResourceFilterAttribute
:
- Spustí se jako první, protože se jedná o filtr prostředků a
ResponseHeaderAttribute
je to filtr akcí. - Zkratuje zbytek kanálu.
Proto se ResponseHeaderAttribute
filtr pro Index
akci nikdy nespustí. Toto chování by bylo stejné, pokud se oba filtry použily na úrovni metody akce za předpokladu, že první ShortCircuitingResourceFilterAttribute
spuštění. První spuštění ShortCircuitingResourceFilterAttribute
z důvodu typu filtru:
[ResponseHeader("Filter-Header", "Filter Value")]
public class ShortCircuitingController : Controller
{
[ShortCircuitingResourceFilter]
public IActionResult Index() =>
Content($"- {nameof(ShortCircuitingController)}.{nameof(Index)}");
}
Injektáž závislostí
Filtry lze přidat podle typu nebo podle instance. Pokud se přidá instance, použije se tato instance pro každý požadavek. Pokud je typ přidaný, aktivuje se typ. Filtr aktivovaný typem znamená:
- Pro každý požadavek se vytvoří instance.
- Všechny závislosti konstruktoru jsou naplněny injektáží závislostí (DI).
Filtry implementované jako atributy a přidané přímo do tříd kontroleru nebo metod akcí nemohou mít závislosti konstruktoru poskytované injektáží závislostí (DI). Závislosti konstruktoru nelze pomocí direktoru poskytnout, protože atributy musí mít parametry konstruktoru zadané tam, kde jsou použity.
Následující filtry podporují závislosti konstruktoru poskytované z DI:
- ServiceFilterAttribute
- TypeFilterAttribute
- IFilterFactory implementovaný u atributu.
Předchozí filtry lze použít u kontroleru nebo akce.
Protokolovací nástroje jsou k dispozici v DI. Vyhněte se ale vytváření a používání filtrů čistě pro účely protokolování. Integrované protokolování architektury obvykle poskytuje to, co je potřeba k protokolování. Protokolování přidané do filtrů:
- Měli byste se zaměřit na obavy nebo chování obchodní domény specifické pro filtr.
- Neměly by protokolovat akce ani jiné události architektury. Integrované filtry už protokoluje akce a události rozhraní.
ServiceFilterAttribute
Typy implementace filtru služby jsou registrovány v Program.cs
. A ServiceFilterAttribute načte instanci filtru z DI.
Následující kód ukazuje LoggingResponseHeaderFilterService
třídu, která používá DI:
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)}");
}
}
V následujícím kódu LoggingResponseHeaderFilterService
se přidá do kontejneru DI:
builder.Services.AddScoped<LoggingResponseHeaderFilterService>();
V následujícím kódu ServiceFilter
načte atribut instanci LoggingResponseHeaderFilterService
filtru z DI:
[ServiceFilter<LoggingResponseHeaderFilterService>]
public IActionResult WithServiceFilter() =>
Content($"- {nameof(FilterDependenciesController)}.{nameof(WithServiceFilter)}");
Při použití ServiceFilterAttribute
, nastavení ServiceFilterAttribute.IsReusable:
- Poskytuje nápovědu, že instance filtru může být znovu použita mimo obor požadavku, který byl vytvořen v rámci. Modul runtime ASP.NET Core nezaručuje:
- Vytvoří se jedna instance filtru.
- V pozdějším okamžiku se filtr z kontejneru DI znovu nepožaduje.
- Neměli byste se používat s filtrem, který závisí na službách s jinou životností než singleton.
ServiceFilterAttribute implementuje IFilterFactory. IFilterFactory
zveřejňuje metodu CreateInstance IFilterMetadata pro vytvoření instance. CreateInstance
načte zadaný typ z DI.
TypeFilterAttribute
TypeFilterAttribute je podobný , ServiceFilterAttributeale jeho typ není vyřešen přímo z kontejneru DI. Vytvoří instanci typu pomocí .Microsoft.Extensions.DependencyInjection.ObjectFactory
Vzhledem k tomu, že TypeFilterAttribute
typy nejsou vyřešeny přímo z kontejneru DI:
- Typy odkazované pomocí kontejneru
TypeFilterAttribute
DI nemusí být zaregistrované. Mají své závislosti splněné kontejnerem DI. TypeFilterAttribute
lze volitelně přijmout argumenty konstruktoru pro typ.
Při použití TypeFilterAttribute
, nastavení TypeFilterAttribute.IsReusable:
Poskytuje nápovědu k opětovnému použití instance filtru mimo obor požadavku, který byl vytvořen v rámci. Modul runtime ASP.NET Core neposkytuje žádné záruky, že se vytvoří jedna instance filtru.
Neměla by se používat s filtrem, který závisí na službách s jinou životností než singleton.
Následující příklad ukazuje, jak předat argumenty do typu pomocí TypeFilterAttribute
:
[TypeFilter(typeof(LoggingResponseHeaderFilter),
Arguments = new object[] { "Filter-Header", "Filter Value" })]
public IActionResult WithTypeFilter() =>
Content($"- {nameof(FilterDependenciesController)}.{nameof(WithTypeFilter)}");
Filtry autorizace
Filtry autorizace:
- Jsou první filtry spuštěné v kanálu filtru.
- Řízení přístupu k metodám akcí
- Mít před metodou, ale ne za metodou.
Vlastní autorizační filtry vyžadují vlastní autorizační architekturu. Preferujte konfiguraci zásad autorizace nebo psaní vlastních zásad autorizace před zápisem vlastního filtru. Předdefinovaný autorizační filtr:
- Volá systém autorizace.
- Neautorizuje žádosti.
Nevyvolávejte výjimky v rámci autorizačních filtrů:
- Výjimka nebude zpracována.
- Filtry výjimek nezpracují výjimku.
Zvažte vydání výzvy, pokud dojde k výjimce ve filtru autorizace.
Přečtěte si další informace o autorizaci.
Filtry prostředků
Filtry prostředků:
- Implementujte buď rozhraní IResourceFilter , nebo IAsyncResourceFilter rozhraní.
- Provádění zabalí většinu kanálu filtru.
- Před filtry prostředků se spouští pouze filtry autorizace.
Filtry prostředků jsou užitečné pro zkratovou část kanálu. Filtr ukládání do mezipaměti se například může vyhnout zbytku kanálu při dosažení mezipaměti.
Příklady filtru prostředků:
Filtr prostředků zkratek zobrazený dříve.
DisableFormValueModelBindingAttribute:
- Zabraňuje vazbě modelu v přístupu k datům formuláře.
- Slouží k nahrání velkých souborů, aby se zabránilo čtení dat formuláře do paměti.
Filtry akcí
Filtry akcí se nevztahují na Razor stránky. Razor Stránky podporují IPageFilter a IAsyncPageFilter. Další informace naleznete v tématu Metody filtrování pro Razor stránky.
Filtry akcí:
- Implementujte buď rozhraní IActionFilter , nebo IAsyncActionFilter rozhraní.
- Jejich spuštění obklopuje provádění metod akcí.
Následující kód ukazuje ukázkový filtr akcí:
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.
}
}
Poskytuje ActionExecutingContext následující vlastnosti:
- ActionArguments - umožňuje čtení vstupů do metody akce.
- Controller – umožňuje manipulaci s instancí kontroleru.
- Result - nastavení
Result
provádění metody akce a následných filtrů akcí.
Vyvolání výjimky v metodě akce:
- Zabraňuje spuštění následných filtrů.
- Na rozdíl od nastavení
Result
se místo úspěšného výsledku považuje za selhání.
Controller
Poskytuje ActionExecutedContext a Result
plus následující vlastnosti:
- Canceled – Hodnota True, pokud provádění akce bylo zkráceno jiným filtrem.
- Exception - Nenulová, pokud akce nebo dříve spuštěný filtr akce vyvolal výjimku. Nastavení této vlastnosti na hodnotu null:
- Efektivně zpracovává výjimku.
Result
se spustí, jako by byla vrácena z metody akce.
Pro volání IAsyncActionFilter
ActionExecutionDelegate:
- Provede všechny následné filtry akcí a metodu akce.
- Vrací objekt
ActionExecutedContext
.
Pokud chcete zkratovat, přiřaďte Microsoft.AspNetCore.Mvc.Filters.ActionExecutingContext.Result k instanci výsledku a nezavolejte next
(the ActionExecutionDelegate
).
Architektura poskytuje abstrakci ActionFilterAttribute , kterou lze podtřídět.
Filtr OnActionExecuting
akcí lze použít k:
- Ověřte stav modelu.
- Pokud je stav neplatný, vraťte chybu.
public class ValidateModelAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
if (!context.ModelState.IsValid)
{
context.Result = new BadRequestObjectResult(context.ModelState);
}
}
}
Poznámka:
Kontrolery anotované atributem [ApiController]
automaticky ověří stav modelu a vrátí odpověď 400. Další informace naleznete v tématu Automatické odpovědi HTTP 400.
Metoda OnActionExecuted
se spustí po metodě akce:
- A může zobrazit a manipulovat s výsledky akce prostřednictvím Result vlastnosti.
- Canceled je nastavena na hodnotu true, pokud provádění akce bylo zkráceno jiným filtrem.
- Exception je nastavena na hodnotu, která není null, pokud akce nebo následný filtr akcí vyvolal výjimku. Nastavení
Exception
na hodnotu null:- Efektivně zpracovává výjimku.
ActionExecutedContext.Result
je spuštěna, jako by byla vrácena normálně z metody akce.
Filtry výjimek
Filtry výjimek:
- Implementace IExceptionFilter nebo IAsyncExceptionFilter.
- Dá se použít k implementaci běžných zásad zpracování chyb.
Následující ukázkový filtr výjimek zobrazí podrobnosti o výjimkách, ke kterým dochází při vývoji aplikace:
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()
};
}
}
Následující kód testuje filtr výjimek:
[TypeFilter<SampleExceptionFilter>]
public class ExceptionController : Controller
{
public IActionResult Index() =>
Content($"- {nameof(ExceptionController)}.{nameof(Index)}");
}
Filtry výjimek:
- Nemáte před a po událostech.
- Implementace OnException nebo OnExceptionAsync.
- Zpracování neošetřených výjimek, ke kterým dochází při Razor vytváření stránky nebo kontroleru, vazbě modelu, filtrech akcí nebo metodách akcí
- Nezachycujte výjimky, ke kterým dochází v filtrech prostředků, filtrech výsledků nebo provádění výsledků MVC.
Pokud chcete zpracovat výjimku, nastavte ExceptionHandled vlastnost na true
vlastnost nebo ji přiřaďte Result . Tím se zastaví šíření výjimky. Filtr výjimek nemůže změnit výjimku na "úspěch". To může udělat jenom filtr akcí.
Filtry výjimek:
- Jsou vhodné pro výjimky zachycení, ke kterým dochází v rámci akcí.
- Nejsou tak flexibilní jako middleware pro zpracování chyb.
Preferujte middleware pro zpracování výjimek. Filtry výjimek použijte pouze v případě, že se zpracování chyb liší podle toho, jakou metodu akce volá. Aplikace může mít například metody akcí pro koncové body rozhraní API i pro zobrazení/HTML. Koncové body rozhraní API můžou vracet informace o chybách jako JSZAPNUTO, zatímco akce založené na zobrazení můžou vrátit chybovou stránku jako HTML.
Filtry výsledků
Filtry výsledků:
- Implementace rozhraní:
- Jejich spuštění obklopuje provádění výsledků akce.
IResultFilter a IAsyncResultFilter
Následující kód ukazuje ukázkový filtr výsledků:
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.
}
}
Druh spuštění výsledku závisí na akci. Akce vracející zobrazení zahrnuje veškeré zpracování razoru jako součást ViewResult provádění. Metoda rozhraní API může v rámci provádění výsledku provádět určité serializace. Přečtěte si další informace o výsledcích akcí.
Filtry výsledků se provádějí pouze v případech, kdy akce nebo filtr akcí vytvoří výsledek akce. Filtry výsledků se nespustí, když:
- Filtr autorizace nebo krátký okruh filtru prostředků kanálem.
- Filtr výjimek zpracovává výjimku tím, že vytvoří výsledek akce.
Metoda Microsoft.AspNetCore.Mvc.Filters.IResultFilter.OnResultExecuting může zkratovat spuštění výsledku akce a následných filtrů výsledků nastavením Microsoft.AspNetCore.Mvc.Filters.ResultExecutingContext.Cancel na true
hodnotu . Při zkratce zapište do objektu odpovědi, abyste se vyhnuli generování prázdné odpovědi. Vyvolání výjimky v IResultFilter.OnResultExecuting
:
- Zabrání spuštění výsledku akce a následných filtrů.
- Místo úspěšného výsledku se považuje za selhání.
Když se Microsoft.AspNetCore.Mvc.Filters.IResultFilter.OnResultExecuted metoda spustí, odpověď již pravděpodobně byla odeslána klientovi. Pokud už byla odpověď odeslána klientovi, nelze ji změnit.
ResultExecutedContext.Canceled
je nastavena na true
to, zda bylo provedení výsledku akce zkráceno jiným filtrem.
ResultExecutedContext.Exception
je nastavena na hodnotu, která není null, pokud výsledek akce nebo následný filtr výsledků vyvolal výjimku. Nastavení Exception
na hodnotu null efektivně zpracuje výjimku a zabrání opětovnému vyvolání výjimky později v kanálu. Neexistuje žádný spolehlivý způsob zápisu dat do odpovědi při zpracování výjimky ve filtru výsledků. Pokud se hlavičky vyprázdnily klientovi, když výsledek akce vyvolá výjimku, neexistuje žádný spolehlivý mechanismus pro odeslání kódu selhání.
V případě IAsyncResultFiltervolání na await next
příkaz ResultExecutionDelegate provede všechny následné filtry výsledků a výsledek akce. Pokud chcete zkratovat, nastavte ResultExecutingContext.Cancel true
a nezavolejte 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;
}
}
}
Architektura poskytuje abstrakci ResultFilterAttribute
, kterou lze podtřídět. Dříve zobrazená třída ResponseHeaderAttribute je příkladem atributu filtru výsledků.
IAlwaysRunResultFilter a IAsyncAlwaysRunResultFilter
IAsyncAlwaysRunResultFilter Rozhraní IAlwaysRunResultFilter deklarují implementaciIResultFilter, která se spouští pro všechny výsledky akce. To zahrnuje výsledky akcí vytvořené tímto:
- Filtry autorizace a filtry prostředků, které jsou zkrácené.
- Filtry výjimek.
Například následující filtr se vždy spustí a nastaví výsledek akce (ObjectResult) se stavovým kódem 422 Nezpracované entity , když selže vyjednávání obsahu:
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 implementuje IFilterMetadata. IFilterFactory
Proto lze instanci použít jako IFilterMetadata
instanci kdekoli v kanálu filtru. Když modul runtime připraví k vyvolání filtru, pokusí se ho přetypovat na .IFilterFactory
Pokud je toto přetypování úspěšné, CreateInstance volá se metoda k vytvoření IFilterMetadata
instance, která je vyvolána. To poskytuje flexibilní návrh, protože při spuštění aplikace není nutné explicitně nastavit přesný kanál filtru.
IFilterFactory.IsReusable
:
- Je nápovědou objektu pro vytváření, že instance filtru vytvořená továrnou může být znovu použita mimo obor požadavku, ve které byla vytvořena.
- Neměla by se používat s filtrem, který závisí na službách s jinou životností než singleton.
Modul runtime ASP.NET Core nezaručuje:
- Vytvoří se jedna instance filtru.
- V pozdějším okamžiku se filtr z kontejneru DI znovu nepožaduje.
Upozorňující
Nakonfigurovat IFilterFactory.IsReusable , aby se vrátil true
pouze v případě, že je zdroj filtrů jednoznačný, filtry jsou bezstavové a filtry jsou bezpečné pro použití napříč více požadavky HTTP. Například nevrací filtry z DI, které jsou registrovány jako vymezené nebo přechodné, pokud IFilterFactory.IsReusable
vrátí true
.
IFilterFactory
lze implementovat pomocí implementací vlastních atributů jako další přístup k vytváření filtrů:
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) { }
}
Filtr se použije v následujícím kódu:
[ResponseHeaderFilterFactory]
public IActionResult Index() =>
Content($"- {nameof(FilterFactoryController)}.{nameof(Index)}");
IFilterFactory implementovaný u atributu
Filtry, které implementují IFilterFactory
, jsou užitečné pro filtry, které:
- Nevyžadují předávání parametrů.
- Mají závislosti konstruktoru, které musí být vyplněny DI.
TypeFilterAttribute implementuje IFilterFactory. IFilterFactory
zveřejňuje metodu CreateInstance IFilterMetadata pro vytvoření instance. CreateInstance
načte zadaný typ z kontejneru služeb (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)}");
}
}
}
Následující kód ukazuje tři přístupy k použití filtru:
[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)}");
V předchozím kódu je upřednostňovaný první přístup k použití filtru.
Použití middlewaru v kanálu filtru
Filtry prostředků fungují podobně jako middleware v tom, že obklopují provádění všeho, co se dodává později v kanálu. Filtry se ale liší od middlewaru v tom, že jsou součástí modulu runtime, což znamená, že mají přístup k kontextu a konstruktorům.
Pokud chcete použít middleware jako filtr, vytvořte typ s metodou Configure
, která určuje middleware, který se má vložit do kanálu filtru. Následující příklad používá middleware k nastavení hlavičky odpovědi:
public class FilterMiddlewarePipeline
{
public void Configure(IApplicationBuilder app)
{
app.Use(async (context, next) =>
{
context.Response.Headers.Add("Pipeline", "Middleware");
await next();
});
}
}
MiddlewareFilterAttribute Použijte ke spuštění middlewaru:
[MiddlewareFilter<FilterMiddlewarePipeline>]
public class FilterMiddlewareController : Controller
{
public IActionResult Index() =>
Content($"- {nameof(FilterMiddlewareController)}.{nameof(Index)}");
}
Filtry middlewaru se spouštějí ve stejné fázi kanálu filtru jako filtry prostředků před vazbou modelu a po zbývající části kanálu.
Bezpečnost vlákna
Při předávání instance filtru do Add
objektu , místo jeho Type
, je filtr singleton a není bezpečný pro přístup z více vláken.
Další materiály
Kirk Larkin, Rick Anderson, Tom Dykstra a Steve Smith
Filtry v ASP.NET Core umožňují spuštění kódu před nebo po konkrétních fázích v kanálu zpracování požadavků.
Předdefinované filtry zpracovávají úlohy, jako jsou:
- Autorizace bránící přístupu k prostředkům, pro které uživatel nemá oprávnění.
- Ukládání odpovědí do mezipaměti, zkratování kanálu požadavku za účelem vrácení odpovědi uložené v mezipaměti
Vlastní filtry je možné vytvořit pro zpracování problémů s křížovým dělením. Mezi průřezové aspekty patří zpracování chyb, ukládání do mezipaměti, konfigurace, autorizace a protokolování. Filtry se vyhýbají duplikování kódu. Filtr výjimek zpracování chyb může například konsolidovat zpracování chyb.
Tento dokument se vztahuje na Razor stránky, kontrolery rozhraní API a kontrolery se zobrazeními. Filtry nefungují přímo se součástmiRazor. Filtr může pouze nepřímo ovlivnit komponentu v případech, kdy:
- Komponenta je vložena do stránky nebo zobrazení.
- Stránka nebo kontroler a zobrazení používají filtr.
Jak fungují filtry
Filtry se spouští v kanálu vyvolání akce ASP.NET Core, někdy označovaného jako kanál filtru. Kanál filtru se spustí po ASP.NET Core vybere akci, která se má provést:
Typy filtrů
Každý typ filtru se provádí v jiné fázi v kanálu filtru:
Filtry autorizace:
- Nejprve spusťte.
- Určete, jestli má uživatel oprávnění k žádosti.
- Zkratovat kanál, pokud požadavek není autorizovaný.
Filtry prostředků:
- Spusťte po autorizaci.
- OnResourceExecuting spustí kód před zbývající částí kanálu filtru. Například
OnResourceExecuting
spustí kód před vazbou modelu. - OnResourceExecuted spustí kód po dokončení zbývající části kanálu.
Filtry akcí:
- Spusťte bezprostředně před a po zavolání metody akce.
- Může změnit argumenty předané do akce.
- Může změnit výsledek vrácený z akce.
- Stránky se nepodporujíRazor.
Filtry koncových bodů:
- Spusťte bezprostředně před a po zavolání metody akce.
- Může změnit argumenty předané do akce.
- Může změnit výsledek vrácený z akce.
- Stránky se nepodporujíRazor.
- Je možné vyvolat jak u akcí, tak u koncových bodů založených na obslužné rutině směrování.
Filtry výjimek aplikují globální zásady na neošetřené výjimky, ke kterým dochází před zápisem textu odpovědi.
Filtry výsledků:
- Spusťte bezprostředně před a po provedení výsledků akce.
- Spusťte pouze v případech, kdy se metoda akce úspěšně provede.
- Jsou užitečné pro logiku, která musí obklopovat provádění zobrazení nebo formátovače.
Následující diagram znázorňuje interakci typů filtrů v kanálu filtru:
Razor Stránky také podporují Razor filtry stránek, které se spouští před a za obslužnou rutinou Razor stránky.
Implementace
Filtry podporují synchronní i asynchronní implementace prostřednictvím různých definic rozhraní.
Synchronní filtry se spouštějí před a po fázi kanálu. Volá se například OnActionExecuting před zavolání metody akce. OnActionExecuted je volána po vrácení metody akce:
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.
}
}
Asynchronní filtry definují metodu On-Stage-ExecutionAsync
. Příklad: 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.
}
}
V předchozím kódu má SampleAsyncActionFilter
znak ActionExecutionDelegate, next
který provede metodu akce.
Více fází filtru
Rozhraní pro více fází filtru lze implementovat v jedné třídě. Například ActionFilterAttribute třída implementuje:
- Synchronní: IActionFilter a IResultFilter
- Asynchronní: IAsyncActionFilter a IAsyncResultFilter
- IOrderedFilter
Implementujte synchronní nebo asynchronní verzi rozhraní filtru, nikoli obě. Modul runtime nejprve zkontroluje, jestli filtr implementuje asynchronní rozhraní, a pokud ano, zavolá to. Pokud ne, volá synchronní metody rozhraní. Pokud jsou asynchronní i synchronní rozhraní implementována v jedné třídě, volá se pouze asynchronní metoda. Při použití abstraktních tříd, jako ActionFilterAttributeje , přepsat pouze synchronní metody nebo asynchronní metody pro každý typ filtru.
Předdefinované atributy filtru
ASP.NET Core obsahuje integrované filtry založené na atributech, které je možné přizpůsobit a podtřídy. Například následující filtr výsledků přidá hlavičku do odpovědi:
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);
}
}
Atributy umožňují filtrům přijímat argumenty, jak je znázorněno v předchozím příkladu. ResponseHeaderAttribute
Použijte metodu kontroleru nebo akce a zadejte název a hodnotu hlavičky HTTP:
[ResponseHeader("Filter-Header", "Filter Value")]
public class ResponseHeaderController : ControllerBase
{
public IActionResult Index() =>
Content("Examine the response headers using the F12 developer tools.");
// ...
K prozkoumání hlaviček použijte nástroj, například vývojářské nástroje prohlížeče. V části Hlavičkyfilter-header: Filter Value
odpovědi se zobrazí.
Následující kód platí ResponseHeaderAttribute
pro kontroler i akci:
[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.");
}
Odpovědi z Multiple
akce zahrnují následující hlavičky:
filter-header: Filter Value
another-filter-header: Another Filter Value
Několik rozhraní filtru má odpovídající atributy, které lze použít jako základní třídy pro vlastní implementace.
Atributy filtru:
- ActionFilterAttribute
- ExceptionFilterAttribute
- ResultFilterAttribute
- FormatFilterAttribute
- ServiceFilterAttribute
- TypeFilterAttribute
Filtry nelze použít u Razor metod obslužné rutiny stránky. Dají se použít buď na Razor model stránky, nebo globálně.
Filtrování oborů a pořadí provádění
Do kanálu je možné přidat filtr v jednom ze tří oborů:
- Použití atributu na kontroleru nebo Razor stránce
- Použití atributu v akci kontroleru Atributy filtru nelze použít u Razor metod obslužné rutiny Pages.
- Globálně pro všechny kontrolery, akce a Razor stránky, jak je znázorněno v následujícím kódu:
var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.AddControllersWithViews(options => { options.Filters.Add<GlobalSampleActionFilter>(); });
Výchozí pořadí provádění
Pokud existuje více filtrů pro určitou fázi kanálu, rozsah určuje výchozí pořadí provádění filtru. Globální filtry obklopují filtry tříd, které následně obklopují filtry metod ohraničí.
V důsledku vnoření filtru se kód filtrů spustí v opačném pořadí před kódem. Posloupnost filtrů:
- Před kódem globálních filtrů.
- Před kódem kontroleru se filtruje.
- Před kódem metody akce filtruje.
- Následující kód metody akce filtruje.
- Kód po kontroleru filtruje.
- Před kódem kontroleru se filtruje.
- Kód po globálních filtrech
Následující příklad znázorňuje pořadí spuštění metod filtru pro synchronní filtry akcí:
Sequence | Obor filtru | Metoda filtru |
---|---|---|
0 | Globální | OnActionExecuting |
2 | Ovladač | OnActionExecuting |
3 | Akce | OnActionExecuting |
4 | Akce | OnActionExecuted |
5 | Ovladač | OnActionExecuted |
6 | Globální | OnActionExecuted |
Filtry na úrovni kontroleru
Každý kontroler, který dědí z Controller , zahrnuje OnActionExecuting, OnActionExecutionAsynca OnActionExecuted metody. Tyto metody zabalí filtry, které se spustí pro danou akci:
OnActionExecuting
spustí se před libovolným filtrem akce.OnActionExecuted
spustí se po všech filtrech akce.OnActionExecutionAsync
spustí se před libovolným filtrem akce. Kód po volání, který senext
spustí po filtrech akce.
Následující ControllerFiltersController
třída:
SampleActionFilterAttribute
Použije ([SampleActionFilter]
) na kontroler.- Přepsání
OnActionExecuting
aOnActionExecuted
.
[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.");
}
}
Navigace pro https://localhost:<port>/ControllerFilters
spuštění následujícího kódu:
ControllerFiltersController.OnActionExecuting
GlobalSampleActionFilter.OnActionExecuting
SampleActionFilterAttribute.OnActionExecuting
ControllerFiltersController.Index
SampleActionFilterAttribute.OnActionExecuted
GlobalSampleActionFilter.OnActionExecuted
ControllerFiltersController.OnActionExecuted
Filtry na úrovni kontroleru nastavily vlastnost Order na int.MinValue
hodnotu . Filtry na úrovni kontroleru nelze nastavit tak, aby se spouštěly po použití filtrů na metody. Pořadí je vysvětleno v další části.
V případě Razor stránek naleznete v tématu Implementace Razor filtrů stránky přepsáním metod filtru.
Přepsání výchozího pořadí
Výchozí posloupnost provádění lze přepsat implementací IOrderedFilter. IOrderedFilter
Order zveřejňuje vlastnost, která má přednost před oborem k určení pořadí provádění. Filtr s nižší Order
hodnotou:
- Spustí před kódem před filtrem s vyšší hodnotou
Order
. - Spustí následující kód po filtru s vyšší
Order
hodnotou.
V příkladu filtrů na úrovni kontroleru má globální obor, GlobalSampleActionFilter
aby se spustil před SampleActionFilterAttribute
, který má obor kontroleru. Pokud chcete provést SampleActionFilterAttribute
spuštění jako první, nastavte jeho pořadí na int.MinValue
:
[SampleActionFilter(Order = int.MinValue)]
public class ControllerFiltersController : Controller
{
// ...
}
Pokud chcete, aby se globální filtr GlobalSampleActionFilter
spustil jako první, nastavte jeho Order
hodnotu na int.MinValue
:
builder.Services.AddControllersWithViews(options =>
{
options.Filters.Add<GlobalSampleActionFilter>(int.MinValue);
});
Zrušení a zkratování
Kanál filtru může být zkrácený nastavením Result vlastnosti parametru ResourceExecutingContext poskytnutého metodě filtru. Například následující filtr prostředků brání spuštění zbytku kanálu:
public class ShortCircuitingResourceFilterAttribute : Attribute, IResourceFilter
{
public void OnResourceExecuting(ResourceExecutingContext context)
{
context.Result = new ContentResult
{
Content = nameof(ShortCircuitingResourceFilterAttribute)
};
}
public void OnResourceExecuted(ResourceExecutedContext context) { }
}
V následujícím kódu cílí metoda akce jak na [ShortCircuitingResourceFilter]
filtr, tak i na metodu [ResponseHeader]
Index
akce. Filtr ShortCircuitingResourceFilterAttribute
:
- Spustí se jako první, protože se jedná o filtr prostředků a
ResponseHeaderAttribute
je to filtr akcí. - Zkratuje zbytek kanálu.
Proto se ResponseHeaderAttribute
filtr pro Index
akci nikdy nespustí. Toto chování by bylo stejné, pokud se oba filtry použily na úrovni metody akce za předpokladu, že první ShortCircuitingResourceFilterAttribute
spuštění. První spuštění ShortCircuitingResourceFilterAttribute
z důvodu typu filtru:
[ResponseHeader("Filter-Header", "Filter Value")]
public class ShortCircuitingController : Controller
{
[ShortCircuitingResourceFilter]
public IActionResult Index() =>
Content($"- {nameof(ShortCircuitingController)}.{nameof(Index)}");
}
Injektáž závislostí
Filtry lze přidat podle typu nebo podle instance. Pokud se přidá instance, použije se tato instance pro každý požadavek. Pokud je typ přidaný, aktivuje se typ. Filtr aktivovaný typem znamená:
- Pro každý požadavek se vytvoří instance.
- Všechny závislosti konstruktoru jsou naplněny injektáží závislostí (DI).
Filtry implementované jako atributy a přidané přímo do tříd kontroleru nebo metod akcí nemohou mít závislosti konstruktoru poskytované injektáží závislostí (DI). Závislosti konstruktoru nelze pomocí direktoru poskytnout, protože atributy musí mít parametry konstruktoru zadané tam, kde jsou použity.
Následující filtry podporují závislosti konstruktoru poskytované z DI:
- ServiceFilterAttribute
- TypeFilterAttribute
- IFilterFactory implementovaný u atributu.
Předchozí filtry lze použít u kontroleru nebo akce.
Protokolovací nástroje jsou k dispozici v DI. Vyhněte se ale vytváření a používání filtrů čistě pro účely protokolování. Integrované protokolování architektury obvykle poskytuje to, co je potřeba k protokolování. Protokolování přidané do filtrů:
- Měli byste se zaměřit na obavy nebo chování obchodní domény specifické pro filtr.
- Neměly by protokolovat akce ani jiné události architektury. Integrované filtry už protokoluje akce a události rozhraní.
ServiceFilterAttribute
Typy implementace filtru služby jsou registrovány v Program.cs
. A ServiceFilterAttribute načte instanci filtru z DI.
Následující kód ukazuje LoggingResponseHeaderFilterService
třídu, která používá DI:
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)}");
}
}
V následujícím kódu LoggingResponseHeaderFilterService
se přidá do kontejneru DI:
builder.Services.AddScoped<LoggingResponseHeaderFilterService>();
V následujícím kódu ServiceFilter
načte atribut instanci LoggingResponseHeaderFilterService
filtru z DI:
[ServiceFilter(typeof(LoggingResponseHeaderFilterService))]
public IActionResult WithServiceFilter() =>
Content($"- {nameof(FilterDependenciesController)}.{nameof(WithServiceFilter)}");
Při použití ServiceFilterAttribute
, nastavení ServiceFilterAttribute.IsReusable:
- Poskytuje nápovědu, že instance filtru může být znovu použita mimo obor požadavku, který byl vytvořen v rámci. Modul runtime ASP.NET Core nezaručuje:
- Vytvoří se jedna instance filtru.
- V pozdějším okamžiku se filtr z kontejneru DI znovu nepožaduje.
- Neměli byste se používat s filtrem, který závisí na službách s jinou životností než singleton.
ServiceFilterAttribute implementuje IFilterFactory. IFilterFactory
zveřejňuje metodu CreateInstance IFilterMetadata pro vytvoření instance. CreateInstance
načte zadaný typ z DI.
TypeFilterAttribute
TypeFilterAttribute je podobný , ServiceFilterAttributeale jeho typ není vyřešen přímo z kontejneru DI. Vytvoří instanci typu pomocí .Microsoft.Extensions.DependencyInjection.ObjectFactory
Vzhledem k tomu, že TypeFilterAttribute
typy nejsou vyřešeny přímo z kontejneru DI:
- Typy odkazované pomocí kontejneru
TypeFilterAttribute
DI nemusí být zaregistrované. Mají své závislosti splněné kontejnerem DI. TypeFilterAttribute
lze volitelně přijmout argumenty konstruktoru pro typ.
Při použití TypeFilterAttribute
, nastavení TypeFilterAttribute.IsReusable:
Poskytuje nápovědu k opětovnému použití instance filtru mimo obor požadavku, který byl vytvořen v rámci. Modul runtime ASP.NET Core neposkytuje žádné záruky, že se vytvoří jedna instance filtru.
Neměla by se používat s filtrem, který závisí na službách s jinou životností než singleton.
Následující příklad ukazuje, jak předat argumenty do typu pomocí TypeFilterAttribute
:
[TypeFilter(typeof(LoggingResponseHeaderFilter),
Arguments = new object[] { "Filter-Header", "Filter Value" })]
public IActionResult WithTypeFilter() =>
Content($"- {nameof(FilterDependenciesController)}.{nameof(WithTypeFilter)}");
Filtry autorizace
Filtry autorizace:
- Jsou první filtry spuštěné v kanálu filtru.
- Řízení přístupu k metodám akcí
- Mít před metodou, ale ne za metodou.
Vlastní autorizační filtry vyžadují vlastní autorizační architekturu. Preferujte konfiguraci zásad autorizace nebo psaní vlastních zásad autorizace před zápisem vlastního filtru. Předdefinovaný autorizační filtr:
- Volá systém autorizace.
- Neautorizuje žádosti.
Nevyvolávejte výjimky v rámci autorizačních filtrů:
- Výjimka nebude zpracována.
- Filtry výjimek nezpracují výjimku.
Zvažte vydání výzvy, pokud dojde k výjimce ve filtru autorizace.
Přečtěte si další informace o autorizaci.
Filtry prostředků
Filtry prostředků:
- Implementujte buď rozhraní IResourceFilter , nebo IAsyncResourceFilter rozhraní.
- Provádění zabalí většinu kanálu filtru.
- Před filtry prostředků se spouští pouze filtry autorizace.
Filtry prostředků jsou užitečné pro zkratovou část kanálu. Filtr ukládání do mezipaměti se například může vyhnout zbytku kanálu při dosažení mezipaměti.
Příklady filtru prostředků:
Filtr prostředků zkratek zobrazený dříve.
DisableFormValueModelBindingAttribute:
- Zabraňuje vazbě modelu v přístupu k datům formuláře.
- Slouží k nahrání velkých souborů, aby se zabránilo čtení dat formuláře do paměti.
Filtry akcí
Filtry akcí se nevztahují na Razor stránky. Razor Stránky podporují IPageFilter a IAsyncPageFilter. Další informace naleznete v tématu Metody filtrování pro Razor stránky.
Filtry akcí:
- Implementujte buď rozhraní IActionFilter , nebo IAsyncActionFilter rozhraní.
- Jejich spuštění obklopuje provádění metod akcí.
Následující kód ukazuje ukázkový filtr akcí:
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.
}
}
Poskytuje ActionExecutingContext následující vlastnosti:
- ActionArguments - umožňuje čtení vstupů do metody akce.
- Controller – umožňuje manipulaci s instancí kontroleru.
- Result - nastavení
Result
provádění metody akce a následných filtrů akcí.
Vyvolání výjimky v metodě akce:
- Zabraňuje spuštění následných filtrů.
- Na rozdíl od nastavení
Result
se místo úspěšného výsledku považuje za selhání.
Controller
Poskytuje ActionExecutedContext a Result
plus následující vlastnosti:
- Canceled – Hodnota True, pokud provádění akce bylo zkráceno jiným filtrem.
- Exception - Nenulová, pokud akce nebo dříve spuštěný filtr akce vyvolal výjimku. Nastavení této vlastnosti na hodnotu null:
- Efektivně zpracovává výjimku.
Result
se spustí, jako by byla vrácena z metody akce.
Pro volání IAsyncActionFilter
ActionExecutionDelegate:
- Provede všechny následné filtry akcí a metodu akce.
- Vrací objekt
ActionExecutedContext
.
Pokud chcete zkratovat, přiřaďte Microsoft.AspNetCore.Mvc.Filters.ActionExecutingContext.Result k instanci výsledku a nezavolejte next
(the ActionExecutionDelegate
).
Architektura poskytuje abstrakci ActionFilterAttribute , kterou lze podtřídět.
Filtr OnActionExecuting
akcí lze použít k:
- Ověřte stav modelu.
- Pokud je stav neplatný, vraťte chybu.
public class ValidateModelAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
if (!context.ModelState.IsValid)
{
context.Result = new BadRequestObjectResult(context.ModelState);
}
}
}
Poznámka:
Kontrolery anotované atributem [ApiController]
automaticky ověří stav modelu a vrátí odpověď 400. Další informace naleznete v tématu Automatické odpovědi HTTP 400.
Metoda OnActionExecuted
se spustí po metodě akce:
- A může zobrazit a manipulovat s výsledky akce prostřednictvím Result vlastnosti.
- Canceled je nastavena na hodnotu true, pokud provádění akce bylo zkráceno jiným filtrem.
- Exception je nastavena na hodnotu, která není null, pokud akce nebo následný filtr akcí vyvolal výjimku. Nastavení
Exception
na hodnotu null:- Efektivně zpracovává výjimku.
ActionExecutedContext.Result
je spuštěna, jako by byla vrácena normálně z metody akce.
Filtry výjimek
Filtry výjimek:
- Implementace IExceptionFilter nebo IAsyncExceptionFilter.
- Dá se použít k implementaci běžných zásad zpracování chyb.
Následující ukázkový filtr výjimek zobrazí podrobnosti o výjimkách, ke kterým dochází při vývoji aplikace:
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()
};
}
}
Následující kód testuje filtr výjimek:
[TypeFilter(typeof(SampleExceptionFilter))]
public class ExceptionController : Controller
{
public IActionResult Index() =>
Content($"- {nameof(ExceptionController)}.{nameof(Index)}");
}
Filtry výjimek:
- Nemáte před a po událostech.
- Implementace OnException nebo OnExceptionAsync.
- Zpracování neošetřených výjimek, ke kterým dochází při Razor vytváření stránky nebo kontroleru, vazbě modelu, filtrech akcí nebo metodách akcí
- Nezachycujte výjimky, ke kterým dochází v filtrech prostředků, filtrech výsledků nebo provádění výsledků MVC.
Pokud chcete zpracovat výjimku, nastavte ExceptionHandled vlastnost na true
vlastnost nebo ji přiřaďte Result . Tím se zastaví šíření výjimky. Filtr výjimek nemůže změnit výjimku na "úspěch". To může udělat jenom filtr akcí.
Filtry výjimek:
- Jsou vhodné pro výjimky zachycení, ke kterým dochází v rámci akcí.
- Nejsou tak flexibilní jako middleware pro zpracování chyb.
Preferujte middleware pro zpracování výjimek. Filtry výjimek použijte pouze v případě, že se zpracování chyb liší podle toho, jakou metodu akce volá. Aplikace může mít například metody akcí pro koncové body rozhraní API i pro zobrazení/HTML. Koncové body rozhraní API můžou vracet informace o chybách jako JSZAPNUTO, zatímco akce založené na zobrazení můžou vrátit chybovou stránku jako HTML.
Filtry výsledků
Filtry výsledků:
- Implementace rozhraní:
- Jejich spuštění obklopuje provádění výsledků akce.
IResultFilter a IAsyncResultFilter
Následující kód ukazuje ukázkový filtr výsledků:
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.
}
}
Druh spuštění výsledku závisí na akci. Akce vracející zobrazení zahrnuje veškeré zpracování razoru jako součást ViewResult provádění. Metoda rozhraní API může v rámci provádění výsledku provádět určité serializace. Přečtěte si další informace o výsledcích akcí.
Filtry výsledků se provádějí pouze v případech, kdy akce nebo filtr akcí vytvoří výsledek akce. Filtry výsledků se nespustí, když:
- Filtr autorizace nebo krátký okruh filtru prostředků kanálem.
- Filtr výjimek zpracovává výjimku tím, že vytvoří výsledek akce.
Metoda Microsoft.AspNetCore.Mvc.Filters.IResultFilter.OnResultExecuting může zkratovat spuštění výsledku akce a následných filtrů výsledků nastavením Microsoft.AspNetCore.Mvc.Filters.ResultExecutingContext.Cancel na true
hodnotu . Při zkratce zapište do objektu odpovědi, abyste se vyhnuli generování prázdné odpovědi. Vyvolání výjimky v IResultFilter.OnResultExecuting
:
- Zabrání spuštění výsledku akce a následných filtrů.
- Místo úspěšného výsledku se považuje za selhání.
Když se Microsoft.AspNetCore.Mvc.Filters.IResultFilter.OnResultExecuted metoda spustí, odpověď již pravděpodobně byla odeslána klientovi. Pokud už byla odpověď odeslána klientovi, nelze ji změnit.
ResultExecutedContext.Canceled
je nastavena na true
to, zda bylo provedení výsledku akce zkráceno jiným filtrem.
ResultExecutedContext.Exception
je nastavena na hodnotu, která není null, pokud výsledek akce nebo následný filtr výsledků vyvolal výjimku. Nastavení Exception
na hodnotu null efektivně zpracuje výjimku a zabrání opětovnému vyvolání výjimky později v kanálu. Neexistuje žádný spolehlivý způsob zápisu dat do odpovědi při zpracování výjimky ve filtru výsledků. Pokud se hlavičky vyprázdnily klientovi, když výsledek akce vyvolá výjimku, neexistuje žádný spolehlivý mechanismus pro odeslání kódu selhání.
V případě IAsyncResultFiltervolání na await next
příkaz ResultExecutionDelegate provede všechny následné filtry výsledků a výsledek akce. Pokud chcete zkratovat, nastavte ResultExecutingContext.Cancel true
a nezavolejte 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;
}
}
}
Architektura poskytuje abstrakci ResultFilterAttribute
, kterou lze podtřídět. Dříve zobrazená třída ResponseHeaderAttribute je příkladem atributu filtru výsledků.
IAlwaysRunResultFilter a IAsyncAlwaysRunResultFilter
IAsyncAlwaysRunResultFilter Rozhraní IAlwaysRunResultFilter deklarují implementaciIResultFilter, která se spouští pro všechny výsledky akce. To zahrnuje výsledky akcí vytvořené tímto:
- Filtry autorizace a filtry prostředků, které jsou zkrácené.
- Filtry výjimek.
Například následující filtr se vždy spustí a nastaví výsledek akce (ObjectResult) se stavovým kódem 422 Nezpracované entity , když selže vyjednávání obsahu:
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 implementuje IFilterMetadata. IFilterFactory
Proto lze instanci použít jako IFilterMetadata
instanci kdekoli v kanálu filtru. Když modul runtime připraví k vyvolání filtru, pokusí se ho přetypovat na .IFilterFactory
Pokud je toto přetypování úspěšné, CreateInstance volá se metoda k vytvoření IFilterMetadata
instance, která je vyvolána. To poskytuje flexibilní návrh, protože při spuštění aplikace není nutné explicitně nastavit přesný kanál filtru.
IFilterFactory.IsReusable
:
- Je nápovědou objektu pro vytváření, že instance filtru vytvořená továrnou může být znovu použita mimo obor požadavku, ve které byla vytvořena.
- Neměla by se používat s filtrem, který závisí na službách s jinou životností než singleton.
Modul runtime ASP.NET Core nezaručuje:
- Vytvoří se jedna instance filtru.
- V pozdějším okamžiku se filtr z kontejneru DI znovu nepožaduje.
Upozorňující
Nakonfigurovat IFilterFactory.IsReusable , aby se vrátil true
pouze v případě, že je zdroj filtrů jednoznačný, filtry jsou bezstavové a filtry jsou bezpečné pro použití napříč více požadavky HTTP. Například nevrací filtry z DI, které jsou registrovány jako vymezené nebo přechodné, pokud IFilterFactory.IsReusable
vrátí true
.
IFilterFactory
lze implementovat pomocí implementací vlastních atributů jako další přístup k vytváření filtrů:
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) { }
}
Filtr se použije v následujícím kódu:
[ResponseHeaderFilterFactory]
public IActionResult Index() =>
Content($"- {nameof(FilterFactoryController)}.{nameof(Index)}");
IFilterFactory implementovaný u atributu
Filtry, které implementují IFilterFactory
, jsou užitečné pro filtry, které:
- Nevyžadují předávání parametrů.
- Mají závislosti konstruktoru, které musí být vyplněny DI.
TypeFilterAttribute implementuje IFilterFactory. IFilterFactory
zveřejňuje metodu CreateInstance IFilterMetadata pro vytvoření instance. CreateInstance
načte zadaný typ z kontejneru služeb (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)}");
}
}
}
Následující kód ukazuje tři přístupy k použití filtru:
[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)}");
V předchozím kódu je upřednostňovaný první přístup k použití filtru.
Použití middlewaru v kanálu filtru
Filtry prostředků fungují podobně jako middleware v tom, že obklopují provádění všeho, co se dodává později v kanálu. Filtry se ale liší od middlewaru v tom, že jsou součástí modulu runtime, což znamená, že mají přístup k kontextu a konstruktorům.
Pokud chcete použít middleware jako filtr, vytvořte typ s metodou Configure
, která určuje middleware, který se má vložit do kanálu filtru. Následující příklad používá middleware k nastavení hlavičky odpovědi:
public class FilterMiddlewarePipeline
{
public void Configure(IApplicationBuilder app)
{
app.Use(async (context, next) =>
{
context.Response.Headers.Add("Pipeline", "Middleware");
await next();
});
}
}
MiddlewareFilterAttribute Použijte ke spuštění middlewaru:
[MiddlewareFilter(typeof(FilterMiddlewarePipeline))]
public class FilterMiddlewareController : Controller
{
public IActionResult Index() =>
Content($"- {nameof(FilterMiddlewareController)}.{nameof(Index)}");
}
Filtry middlewaru se spouštějí ve stejné fázi kanálu filtru jako filtry prostředků před vazbou modelu a po zbývající části kanálu.
Bezpečnost vlákna
Při předávání instance filtru do Add
objektu , místo jeho Type
, je filtr singleton a není bezpečný pro přístup z více vláken.
Další materiály
Kirk Larkin, Rick Anderson, Tom Dykstra a Steve Smith
Filtry v ASP.NET Core umožňují spuštění kódu před nebo po konkrétních fázích v kanálu zpracování požadavků.
Předdefinované filtry zpracovávají úlohy, jako jsou:
- Autorizace bránící přístupu k prostředkům, pro které uživatel nemá oprávnění.
- Ukládání odpovědí do mezipaměti, zkratování kanálu požadavku za účelem vrácení odpovědi uložené v mezipaměti
Vlastní filtry je možné vytvořit pro zpracování problémů s křížovým dělením. Mezi průřezové aspekty patří zpracování chyb, ukládání do mezipaměti, konfigurace, autorizace a protokolování. Filtry se vyhýbají duplikování kódu. Filtr výjimek zpracování chyb může například konsolidovat zpracování chyb.
Tento dokument se vztahuje na Razor stránky, kontrolery rozhraní API a kontrolery se zobrazeními. Filtry nefungují přímo se součástmiRazor. Filtr může pouze nepřímo ovlivnit komponentu v případech, kdy:
- Komponenta je vložena do stránky nebo zobrazení.
- Stránka nebo kontroler a zobrazení používají filtr.
Zobrazení nebo stažení ukázky (postup stažení)
Jak fungují filtry
Filtry se spouští v kanálu vyvolání akce ASP.NET Core, někdy označovaného jako kanál filtru. Kanál filtru se spustí po ASP.NET Core vybere akci, která se má spustit.
Typy filtrů
Každý typ filtru se provádí v jiné fázi v kanálu filtru:
Nejprve se spustí autorizační filtry a slouží k určení, jestli má uživatel k žádosti autorizaci. Autorizace filtruje zkratový okruh kanálu, pokud požadavek není autorizovaný.
Filtry prostředků:
- Spusťte po autorizaci.
- OnResourceExecuting spustí kód před zbývající částí kanálu filtru. Například
OnResourceExecuting
spustí kód před vazbou modelu. - OnResourceExecuted spustí kód po dokončení zbývající části kanálu.
Filtry akcí:
- Spusťte kód bezprostředně před a po zavolání metody akce.
- Může změnit argumenty předané do akce.
- Může změnit výsledek vrácený z akce.
- Stránky se nepodporujíRazor.
Filtry výjimek aplikují globální zásady na neošetřené výjimky, ke kterým dochází před zápisem textu odpovědi.
Filtry výsledků spustí kód bezprostředně před a po provedení výsledků akce. Spustí se pouze tehdy, když se metoda akce úspěšně spustila. Jsou užitečné pro logiku, která musí obklopovat provádění zobrazení nebo formátovače.
Následující diagram znázorňuje interakci typů filtrů v kanálu filtru.
Implementace
Filtry podporují synchronní i asynchronní implementace prostřednictvím různých definic rozhraní.
Synchronní filtry spouští kód před a po fázi kanálu. Volá se například OnActionExecuting před zavolání metody akce. OnActionExecuted je volána po vrácení metody akce.
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);
}
}
V předchozím kódu je MyDebug funkce nástroje ve stažení ukázky.
Asynchronní filtry definují metodu On-Stage-ExecutionAsync
. Příklad: 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.
}
}
V předchozím kódu SampleAsyncActionFilter
má ActionExecutionDelegate (next
), která spouští metodu akce.
Více fází filtru
Rozhraní pro více fází filtru lze implementovat v jedné třídě. Například ActionFilterAttribute třída implementuje:
- Synchronní: IActionFilter a IResultFilter
- Asynchronní: IAsyncActionFilter a IAsyncResultFilter
- IOrderedFilter
Implementujte synchronní nebo asynchronní verzi rozhraní filtru, nikoli obě. Modul runtime nejprve zkontroluje, jestli filtr implementuje asynchronní rozhraní, a pokud ano, zavolá to. Pokud ne, volá synchronní metody rozhraní. Pokud jsou asynchronní i synchronní rozhraní implementována v jedné třídě, volá se pouze asynchronní metoda. Při použití abstraktních tříd, jako ActionFilterAttributeje , přepsat pouze synchronní metody nebo asynchronní metody pro každý typ filtru.
Předdefinované atributy filtru
ASP.NET Core obsahuje integrované filtry založené na atributech, které je možné přizpůsobit a podtřídy. Například následující filtr výsledků přidá hlavičku do odpovědi:
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);
}
}
Atributy umožňují filtrům přijímat argumenty, jak je znázorněno v předchozím příkladu. AddHeaderAttribute
Použijte metodu kontroleru nebo akce a zadejte název a hodnotu hlavičky HTTP:
[AddHeader("Author", "Rick Anderson")]
public class SampleController : Controller
{
public IActionResult Index()
{
return Content("Examine the headers using the F12 developer tools.");
}
K prozkoumání hlaviček použijte nástroj, například vývojářské nástroje prohlížeče. V části Hlavičkyauthor: Rick Anderson
odpovědi se zobrazí.
Následující kód implementuje následující ActionFilterAttribute
kód:
- Přečte název a název z konfiguračního systému. Na rozdíl od předchozí ukázky následující kód nevyžaduje přidání parametrů filtru do kódu.
- Přidá název a název do hlavičky odpovědi.
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);
}
}
Možnosti konfigurace jsou k dispozici v konfiguračním systému pomocí vzoru možností. Například ze appsettings.json
souboru:
{
"Position": {
"Title": "Editor",
"Name": "Joe Smith"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*"
}
V :StartUp.ConfigureServices
- Třída
PositionOptions
se přidá do kontejneru služby s"Position"
oblastí konfigurace. - Přidá se
MyActionFilterAttribute
do kontejneru služby.
public void ConfigureServices(IServiceCollection services)
{
services.Configure<PositionOptions>(
Configuration.GetSection("Position"));
services.AddScoped<MyActionFilterAttribute>();
services.AddControllersWithViews();
}
Následující kód ukazuje PositionOptions
třídu:
public class PositionOptions
{
public string Title { get; set; }
public string Name { get; set; }
}
Následující kód se vztahuje na metodu MyActionFilterAttribute
Index2
:
[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.");
}
V části Hlavičkyauthor: Rick Anderson
odpovědi se zobrazí po Editor: Joe Smith
zavolání koncového Sample/Index2
bodu.
Následující kód se vztahuje na MyActionFilterAttribute
stránku a na AddHeaderAttribute
Razor stránku:
[AddHeader("Author", "Rick Anderson")]
[ServiceFilter(typeof(MyActionFilterAttribute))]
public class IndexModel : PageModel
{
public void OnGet()
{
}
}
Filtry nelze použít u Razor metod obslužné rutiny stránky. Dají se použít buď na Razor model stránky, nebo globálně.
Několik rozhraní filtru má odpovídající atributy, které lze použít jako základní třídy pro vlastní implementace.
Atributy filtru:
- ActionFilterAttribute
- ExceptionFilterAttribute
- ResultFilterAttribute
- FormatFilterAttribute
- ServiceFilterAttribute
- TypeFilterAttribute
Filtrování oborů a pořadí provádění
Do kanálu je možné přidat filtr v jednom ze tří oborů:
- Použití atributu v akci kontroleru Atributy filtru nelze použít u Razor metod obslužné rutiny Pages.
- Použití atributu na kontroleru nebo Razor stránce
- Globálně pro všechny kontrolery, akce a Razor stránky, jak je znázorněno v následujícím kódu:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews(options =>
{
options.Filters.Add(typeof(MySampleActionFilter));
});
}
Výchozí pořadí provádění
Pokud existuje více filtrů pro určitou fázi kanálu, rozsah určuje výchozí pořadí provádění filtru. Globální filtry obklopují filtry tříd, které následně obklopují filtry metod ohraničí.
V důsledku vnoření filtru se kód filtrů spustí v opačném pořadí před kódem. Posloupnost filtrů:
- Před kódem globálních filtrů.
- Před kódem kontroleru a Razor filtru stránky.
- Před kódem metody akce filtruje.
- Následující kód metody akce filtruje.
- Kód po kontroleru a Razor filtrech stránek.
- Před kódem kontroleru a Razor filtru stránky.
- Kód po globálních filtrech
Následující příklad znázorňuje pořadí, ve kterém jsou metody filtru volány pro synchronní filtry akcí.
Sequence | Obor filtru | Metoda filtru |
---|---|---|
0 | Globální | OnActionExecuting |
2 | Kontroler nebo Razor stránka | OnActionExecuting |
3 | metoda | OnActionExecuting |
4 | metoda | OnActionExecuted |
5 | Kontroler nebo Razor stránka | OnActionExecuted |
6 | Globální | OnActionExecuted |
Filtry na úrovni kontroleru
Každý kontroler, který dědí ze Controller základní třídy, zahrnuje Controller.OnActionExecuting, Controller.OnActionExecutionAsynca Controller.OnActionExecutedOnActionExecuted
metody. Tyto metody:
- Zabalte filtry, které se spustí pro danou akci.
OnActionExecuting
se volá před filtry některé akce.OnActionExecuted
se volá po všech filtrech akcí.OnActionExecutionAsync
se volá před filtry některé akce. Kód ve filtru ponext
spuštění po provedení metody akce.
Například v ukázce MySampleActionFilter
stahování se použije globálně při spuštění.
Pomocná rutina TestController
:
SampleActionFilterAttribute
Použije ([SampleActionFilter]
) naFilterTest2
akci.- Přepsání
OnActionExecuting
aOnActionExecuted
.
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 poskytuje balíček NuGet Rick.Docs.Samples.RouteInfo a zobrazí informace o trase.
Navigace pro https://localhost:5001/Test/FilterTest2
spuštění následujícího kódu:
TestController.OnActionExecuting
MySampleActionFilter.OnActionExecuting
SampleActionFilterAttribute.OnActionExecuting
TestController.FilterTest2
SampleActionFilterAttribute.OnActionExecuted
MySampleActionFilter.OnActionExecuted
TestController.OnActionExecuted
Filtry na úrovni kontroleru nastavily vlastnost Order na int.MinValue
hodnotu . Filtry na úrovni kontroleru nelze nastavit tak, aby se spouštěly po použití filtrů na metody. Pořadí je vysvětleno v další části.
V případě Razor stránek naleznete v tématu Implementace Razor filtrů stránky přepsáním metod filtru.
Přepsání výchozího pořadí
Výchozí posloupnost provádění lze přepsat implementací IOrderedFilter. IOrderedFilter
Order zveřejňuje vlastnost, která má přednost před oborem k určení pořadí provádění. Filtr s nižší Order
hodnotou:
- Spustí před kódem před filtrem s vyšší hodnotou
Order
. - Spustí následující kód po filtru s vyšší
Order
hodnotou.
Vlastnost Order
je nastavena s parametrem konstruktoru:
[SampleActionFilter(Order = int.MinValue)]
Vezměte v úvahu dva filtry akcí v následujícím kontroleru:
[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);
}
}
Globální filtr se přidá do StartUp.ConfigureServices
:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews(options =>
{
options.Filters.Add(typeof(MySampleActionFilter));
});
}
Filtry 3 se spouští v následujícím pořadí:
Test2Controller.OnActionExecuting
MySampleActionFilter.OnActionExecuting
MyAction2FilterAttribute.OnActionExecuting
Test2Controller.FilterTest2
MyAction2FilterAttribute.OnResultExecuting
MySampleActionFilter.OnActionExecuted
Test2Controller.OnActionExecuted
Vlastnost Order
přepíše obor při určování pořadí, ve kterém se filtry spouští. Filtry se seřadí jako první podle pořadí a pak se obor použije k přerušení vazeb. Všechny integrované filtry implementují IOrderedFilter
a nastavují výchozí Order
hodnotu na 0. Jak už bylo zmíněno dříve, filtry na úrovni kontroleru nastavily vlastnost Order na int.MinValue
Předdefinované filtry, rozsah určuje pořadí, pokud Order
není nastavena na nenulovou hodnotu.
V předchozím kódu má globální obor, MySampleActionFilter
aby se spustil před MyAction2FilterAttribute
, který má obor kontroleru. Pokud chcete provést MyAction2FilterAttribute
spuštění jako první, nastavte pořadí na 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);
}
}
Pokud chcete, aby se globální filtr MySampleActionFilter
spustil jako první, nastavte Order
na int.MinValue
:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews(options =>
{
options.Filters.Add(typeof(MySampleActionFilter),
int.MinValue);
});
}
Zrušení a zkratování
Kanál filtru může být zkrácený nastavením Result vlastnosti parametru ResourceExecutingContext poskytnutého metodě filtru. Například následující filtr prostředků brání spuštění zbytku kanálu:
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)
{
}
}
V následujícím kódu cílí metoda akce jak na ShortCircuitingResourceFilter
filtr, tak i na metodu AddHeader
SomeResource
akce. Pomocná rutina ShortCircuitingResourceFilter
:
- Spustí se jako první, protože se jedná o filtr prostředků a
AddHeader
je to filtr akcí. - Zkratuje zbytek kanálu.
Proto se AddHeader
filtr pro SomeResource
akci nikdy nespustí. Toto chování by bylo stejné, pokud se oba filtry použily na úrovni metody akce za předpokladu, že první ShortCircuitingResourceFilter
spuštění. Spustí se ShortCircuitingResourceFilter
jako první z důvodu typu filtru nebo explicitním použitím Order
vlastnosti.
[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.");
}
}
Injektáž závislostí
Filtry lze přidat podle typu nebo podle instance. Pokud se přidá instance, použije se tato instance pro každý požadavek. Pokud je typ přidaný, aktivuje se typ. Filtr aktivovaný typem znamená:
- Pro každý požadavek se vytvoří instance.
- Všechny závislosti konstruktoru jsou naplněny injektáží závislostí (DI).
Filtry implementované jako atributy a přidané přímo do tříd kontroleru nebo metod akcí nemohou mít závislosti konstruktoru poskytované injektáží závislostí (DI). Di nemůže poskytnout závislosti konstruktoru, protože:
- Atributy musí mít parametry konstruktoru zadané tam, kde se použijí.
- Toto je omezení fungování atributů.
Následující filtry podporují závislosti konstruktoru poskytované z DI:
- ServiceFilterAttribute
- TypeFilterAttribute
- IFilterFactory implementovaný u atributu.
Předchozí filtry lze použít pro kontroler nebo metodu akce:
Protokolovací nástroje jsou k dispozici v DI. Vyhněte se ale vytváření a používání filtrů čistě pro účely protokolování. Integrované protokolování architektury obvykle poskytuje to, co je potřeba k protokolování. Protokolování přidané do filtrů:
- Měli byste se zaměřit na obavy nebo chování obchodní domény specifické pro filtr.
- Neměly by protokolovat akce ani jiné události architektury. Předdefinované filtry akcí protokolu a událostí architektury.
ServiceFilterAttribute
Typy implementace filtru služby jsou registrovány v ConfigureServices
. A ServiceFilterAttribute načte instanci filtru z DI.
Následující kód ukazuje 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");
}
}
V následujícím kódu AddHeaderResultServiceFilter
se přidá do kontejneru 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
});
}
V následujícím kódu ServiceFilter
načte atribut instanci AddHeaderResultServiceFilter
filtru z DI:
[ServiceFilter(typeof(AddHeaderResultServiceFilter))]
public IActionResult Index()
{
return View();
}
Při použití ServiceFilterAttribute
, nastavení ServiceFilterAttribute.IsReusable:
Poskytuje nápovědu, že instance filtru může být znovu použita mimo obor požadavku, který byl vytvořen v rámci. Modul runtime ASP.NET Core nezaručuje:
- Vytvoří se jedna instance filtru.
- V pozdějším okamžiku se filtr z kontejneru DI znovu nepožaduje.
Neměla by se používat s filtrem, který závisí na službách s jinou životností než singleton.
ServiceFilterAttribute implementuje IFilterFactory. IFilterFactory
zveřejňuje metodu CreateInstance IFilterMetadata pro vytvoření instance. CreateInstance
načte zadaný typ z DI.
TypeFilterAttribute
TypeFilterAttribute je podobný , ServiceFilterAttributeale jeho typ není vyřešen přímo z kontejneru DI. Vytvoří instanci typu pomocí .Microsoft.Extensions.DependencyInjection.ObjectFactory
Vzhledem k tomu, že TypeFilterAttribute
typy nejsou vyřešeny přímo z kontejneru DI:
- Typy odkazované pomocí kontejneru
TypeFilterAttribute
DI nemusí být zaregistrované. Mají své závislosti splněné kontejnerem DI. TypeFilterAttribute
lze volitelně přijmout argumenty konstruktoru pro typ.
Při použití TypeFilterAttribute
, nastavení TypeFilterAttribute.IsReusable:
Poskytuje nápovědu k opětovnému použití instance filtru mimo obor požadavku, který byl vytvořen v rámci. Modul runtime ASP.NET Core neposkytuje žádné záruky, že se vytvoří jedna instance filtru.
Neměla by se používat s filtrem, který závisí na službách s jinou životností než singleton.
Následující příklad ukazuje, jak předat argumenty do typu pomocí TypeFilterAttribute
:
[TypeFilter(typeof(LogConstantFilter),
Arguments = new object[] { "Method 'Hi' called" })]
public IActionResult Hi(string name)
{
return Content($"Hi {name}");
}
Filtry autorizace
Filtry autorizace:
- Jsou první filtry spuštěné v kanálu filtru.
- Řízení přístupu k metodám akcí
- Mít před metodou, ale ne za metodou.
Vlastní autorizační filtry vyžadují vlastní autorizační architekturu. Preferujte konfiguraci zásad autorizace nebo psaní vlastních zásad autorizace před zápisem vlastního filtru. Předdefinovaný autorizační filtr:
- Volá systém autorizace.
- Neautorizuje žádosti.
Nevyvolávejte výjimky v rámci autorizačních filtrů:
- Výjimka nebude zpracována.
- Filtry výjimek nezpracují výjimku.
Zvažte vydání výzvy, pokud dojde k výjimce ve filtru autorizace.
Přečtěte si další informace o autorizaci.
Filtry prostředků
Filtry prostředků:
- Implementujte buď rozhraní IResourceFilter , nebo IAsyncResourceFilter rozhraní.
- Provádění zabalí většinu kanálu filtru.
- Před filtry prostředků se spouští pouze filtry autorizace.
Filtry prostředků jsou užitečné pro zkratovou část kanálu. Filtr ukládání do mezipaměti se například může vyhnout zbytku kanálu při dosažení mezipaměti.
Příklady filtru prostředků:
Filtr prostředků zkratek zobrazený dříve.
DisableFormValueModelBindingAttribute:
- Zabraňuje vazbě modelu v přístupu k datům formuláře.
- Slouží k nahrání velkých souborů, aby se zabránilo čtení dat formuláře do paměti.
Filtry akcí
Filtry akcí se nevztahují na Razor stránky. Razor Stránky podporují IPageFilter a IAsyncPageFilter. Další informace naleznete v tématu Metody filtrování pro Razor stránky.
Filtry akcí:
- Implementujte buď rozhraní IActionFilter , nebo IAsyncActionFilter rozhraní.
- Jejich spuštění obklopuje provádění metod akcí.
Následující kód ukazuje ukázkový filtr akcí:
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);
}
}
Poskytuje ActionExecutingContext následující vlastnosti:
- ActionArguments - umožňuje čtení vstupů do metody akce.
- Controller – umožňuje manipulaci s instancí kontroleru.
- Result - nastavení
Result
provádění metody akce a následných filtrů akcí.
Vyvolání výjimky v metodě akce:
- Zabraňuje spuštění následných filtrů.
- Na rozdíl od nastavení
Result
se místo úspěšného výsledku považuje za selhání.
Controller
Poskytuje ActionExecutedContext a Result
plus následující vlastnosti:
Canceled – Hodnota True, pokud provádění akce bylo zkráceno jiným filtrem.
Exception - Nenulová, pokud akce nebo dříve spuštěný filtr akce vyvolal výjimku. Nastavení této vlastnosti na hodnotu null:
- Efektivně zpracovává výjimku.
Result
se spustí, jako by byla vrácena z metody akce.
Pro volání IAsyncActionFilter
ActionExecutionDelegate:
- Provede všechny následné filtry akcí a metodu akce.
- Vrací objekt
ActionExecutedContext
.
Pokud chcete zkratovat, přiřaďte Microsoft.AspNetCore.Mvc.Filters.ActionExecutingContext.Result k instanci výsledku a nezavolejte next
(the ActionExecutionDelegate
).
Architektura poskytuje abstrakci ActionFilterAttribute , kterou lze podtřídět.
Filtr OnActionExecuting
akcí lze použít k:
- Ověřte stav modelu.
- Pokud je stav neplatný, vraťte chybu.
public class ValidateModelAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext
context)
{
if (!context.ModelState.IsValid)
{
context.Result = new BadRequestObjectResult(
context.ModelState);
}
}
Poznámka:
Kontrolery anotované atributem [ApiController]
automaticky ověří stav modelu a vrátí odpověď 400. Další informace naleznete v tématu Automatické odpovědi HTTP 400.
Metoda OnActionExecuted
se spustí po metodě akce:
A může zobrazit a manipulovat s výsledky akce prostřednictvím Result vlastnosti.
Canceled je nastavena na hodnotu true, pokud provádění akce bylo zkráceno jiným filtrem.
Exception je nastavena na hodnotu, která není null, pokud akce nebo následný filtr akcí vyvolal výjimku. Nastavení
Exception
na hodnotu null:- Efektivně zpracovává výjimku.
ActionExecutedContext.Result
je spuštěna, jako by byla vrácena normálně z metody akce.
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);
}
}
Filtry výjimek
Filtry výjimek:
- Implementace IExceptionFilter nebo IAsyncExceptionFilter.
- Dá se použít k implementaci běžných zásad zpracování chyb.
Následující ukázkový filtr výjimek používá vlastní zobrazení chyb k zobrazení podrobností o výjimkách, ke kterým dochází při vývoji aplikace:
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;
}
}
Následující kód testuje filtr výjimek:
[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.");
}
}
Filtry výjimek:
- Nemáte před a po událostech.
- Implementace OnException nebo OnExceptionAsync.
- Zpracování neošetřených výjimek, ke kterým dochází při Razor vytváření stránky nebo kontroleru, vazbě modelu, filtrech akcí nebo metodách akcí
- Nezachycujte výjimky, ke kterým dochází v filtrech prostředků, filtrech výsledků nebo provádění výsledků MVC.
Pokud chcete zpracovat výjimku, nastavte ExceptionHandled vlastnost na true
vlastnost nebo ji přiřaďte Result . Tím se zastaví šíření výjimky. Filtr výjimek nemůže změnit výjimku na "úspěch". To může udělat jenom filtr akcí.
Filtry výjimek:
- Jsou vhodné pro výjimky zachycení, ke kterým dochází v rámci akcí.
- Nejsou tak flexibilní jako middleware pro zpracování chyb.
Preferujte middleware pro zpracování výjimek. Filtry výjimek použijte pouze v případě, že se zpracování chyb liší podle toho, jakou metodu akce volá. Aplikace může mít například metody akcí pro koncové body rozhraní API i pro zobrazení/HTML. Koncové body rozhraní API můžou vracet informace o chybách jako JSZAPNUTO, zatímco akce založené na zobrazení můžou vrátit chybovou stránku jako HTML.
Filtry výsledků
Filtry výsledků:
- Implementace rozhraní:
- Jejich spuštění obklopuje provádění výsledků akce.
IResultFilter a IAsyncResultFilter
Následující kód ukazuje filtr výsledků, který přidá hlavičku 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");
}
}
Druh spuštění výsledku závisí na akci. Akce vracející zobrazení zahrnuje veškeré zpracování razoru jako součást ViewResult provádění. Metoda rozhraní API může v rámci provádění výsledku provádět určité serializace. Přečtěte si další informace o výsledcích akcí.
Filtry výsledků se provádějí pouze v případech, kdy akce nebo filtr akcí vytvoří výsledek akce. Filtry výsledků se nespustí, když:
- Filtr autorizace nebo krátký okruh filtru prostředků kanálem.
- Filtr výjimek zpracovává výjimku tím, že vytvoří výsledek akce.
Metoda Microsoft.AspNetCore.Mvc.Filters.IResultFilter.OnResultExecuting může zkratovat spuštění výsledku akce a následných filtrů výsledků nastavením Microsoft.AspNetCore.Mvc.Filters.ResultExecutingContext.Cancel na true
hodnotu . Při zkratce zapište do objektu odpovědi, abyste se vyhnuli generování prázdné odpovědi. Vyvolání výjimky v IResultFilter.OnResultExecuting
:
- Zabrání spuštění výsledku akce a následných filtrů.
- Místo úspěšného výsledku se považuje za selhání.
Když se Microsoft.AspNetCore.Mvc.Filters.IResultFilter.OnResultExecuted metoda spustí, odpověď již pravděpodobně byla odeslána klientovi. Pokud už byla odpověď odeslána klientovi, nelze ji změnit.
ResultExecutedContext.Canceled
je nastavena na true
to, zda bylo provedení výsledku akce zkráceno jiným filtrem.
ResultExecutedContext.Exception
je nastavena na hodnotu, která není null, pokud výsledek akce nebo následný filtr výsledků vyvolal výjimku. Nastavení Exception
na hodnotu null efektivně zpracuje výjimku a zabrání opětovnému vyvolání výjimky později v kanálu. Neexistuje žádný spolehlivý způsob zápisu dat do odpovědi při zpracování výjimky ve filtru výsledků. Pokud se hlavičky vyprázdnily klientovi, když výsledek akce vyvolá výjimku, neexistuje žádný spolehlivý mechanismus pro odeslání kódu selhání.
V případě IAsyncResultFiltervolání na await next
příkaz ResultExecutionDelegate provede všechny následné filtry výsledků a výsledek akce. Pokud chcete zkratovat, nastavte ResultExecutingContext.Cancel true
a nezavolejte ResultExecutionDelegate
:
public class MyAsyncResponseFilter : IAsyncResultFilter
{
public async Task OnResultExecutionAsync(ResultExecutingContext context,
ResultExecutionDelegate next)
{
if (!(context.Result is EmptyResult))
{
await next();
}
else
{
context.Cancel = true;
}
}
}
Architektura poskytuje abstrakci ResultFilterAttribute
, kterou lze podtřídět. Třída AddHeaderAttribute zobrazená dříve je příkladem atributu filtru výsledků.
IAlwaysRunResultFilter a IAsyncAlwaysRunResultFilter
IAsyncAlwaysRunResultFilter Rozhraní IAlwaysRunResultFilter deklarují implementaciIResultFilter, která se spouští pro všechny výsledky akce. To zahrnuje výsledky akcí vytvořené tímto:
- Filtry autorizace a filtry prostředků, které jsou zkrácené.
- Filtry výjimek.
Například následující filtr se vždy spustí a nastaví výsledek akce (ObjectResult) se stavovým kódem 422 Nezpracované entity , když selže vyjednávání obsahu:
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 implementuje IFilterMetadata. IFilterFactory
Proto lze instanci použít jako IFilterMetadata
instanci kdekoli v kanálu filtru. Když modul runtime připraví k vyvolání filtru, pokusí se ho přetypovat na .IFilterFactory
Pokud je toto přetypování úspěšné, CreateInstance volá se metoda k vytvoření IFilterMetadata
instance, která je vyvolána. To poskytuje flexibilní návrh, protože při spuštění aplikace není nutné explicitně nastavit přesný kanál filtru.
IFilterFactory.IsReusable
:
- Je nápovědou objektu pro vytváření, že instance filtru vytvořená továrnou může být znovu použita mimo obor požadavku, ve které byla vytvořena.
- Neměla by se používat s filtrem, který závisí na službách s jinou životností než singleton.
Modul runtime ASP.NET Core nezaručuje:
- Vytvoří se jedna instance filtru.
- V pozdějším okamžiku se filtr z kontejneru DI znovu nepožaduje.
Upozorňující
Nakonfigurovat IFilterFactory.IsReusable , aby se vrátil true
pouze v případě, že je zdroj filtrů jednoznačný, filtry jsou bezstavové a filtry jsou bezpečné pro použití napříč více požadavky HTTP. Například nevrací filtry z DI, které jsou registrovány jako vymezené nebo přechodné, pokud IFilterFactory.IsReusable
vrátí true
.
IFilterFactory
lze implementovat pomocí implementací vlastních atributů jako další přístup k vytváření filtrů:
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;
}
}
}
Filtr se použije v následujícím kódu:
[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.");
}
}
Otestujte předchozí kód spuštěním ukázky stahování:
- Vyvoláte vývojářské nástroje F12.
- Přejděte na
https://localhost:5001/Sample/HeaderWithFactory
.
Vývojářské nástroje F12 zobrazují následující hlavičky odpovědi přidané ukázkovým kódem:
- autor:
Rick Anderson
- globaladdheader:
Result filter added to MvcOptions.Filters
- interní:
My header
Předchozí kód vytvoří interní hlavičku odpovědi: My header
IFilterFactory implementovaný u atributu
Filtry, které implementují IFilterFactory
, jsou užitečné pro filtry, které:
- Nevyžadují předávání parametrů.
- Mají závislosti konstruktoru, které musí být vyplněny DI.
TypeFilterAttribute implementuje IFilterFactory. IFilterFactory
zveřejňuje metodu CreateInstance IFilterMetadata pro vytvoření instance. CreateInstance
načte zadaný typ z kontejneru služeb (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");
}
}
}
Následující kód ukazuje tři přístupy k použití [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");
}
V předchozím kódu je zdobení metody [SampleActionFilter]
upřednostňovaným přístupem k použití SampleActionFilter
.
Použití middlewaru v kanálu filtru
Filtry prostředků fungují podobně jako middleware v tom, že obklopují provádění všeho, co se dodává později v kanálu. Filtry se ale liší od middlewaru v tom, že jsou součástí modulu runtime, což znamená, že mají přístup k kontextu a konstruktorům.
Pokud chcete použít middleware jako filtr, vytvořte typ s metodou Configure
, která určuje middleware, který se má vložit do kanálu filtru. Následující příklad používá middleware lokalizace k vytvoření aktuální jazykové verze požadavku:
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);
}
}
MiddlewareFilterAttribute Použijte ke spuštění middlewaru:
[Route("{culture}/[controller]/[action]")]
[MiddlewareFilter(typeof(LocalizationPipeline))]
public IActionResult CultureFromRouteData()
{
return Content(
$"CurrentCulture:{CultureInfo.CurrentCulture.Name},"
+ $"CurrentUICulture:{CultureInfo.CurrentUICulture.Name}");
}
Filtry middlewaru se spouštějí ve stejné fázi kanálu filtru jako filtry prostředků před vazbou modelu a po zbývající části kanálu.
Bezpečnost vlákna
Při předávání instance filtru do Add
objektu , místo jeho Type
, je filtr singleton a není bezpečný pro přístup z více vláken.
Další akce
- Viz Metody filtru pro Razor stránky.
- Pokud chcete experimentovat s filtry, stáhněte, otestujte a upravte ukázku GitHubu.