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:

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

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:

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

Razor 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, nextkterý 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:

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:

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.
  • 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
1 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ý se next spustí po filtrech akce.

Následující ControllerFiltersController třída:

  • SampleActionFilterAttribute Použije ([SampleActionFilter]) na kontroler.
  • Přepsání OnActionExecuting a OnActionExecuted.
[SampleActionFilter]
public class ControllerFiltersController : Controller
{
    public override void OnActionExecuting(ActionExecutingContext context)
    {
        Console.WriteLine(
            $"- {nameof(ControllerFiltersController)}.{nameof(OnActionExecuting)}");

        base.OnActionExecuting(context);
    }

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

        base.OnActionExecuted(context);
    }

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

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

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.MinValuehodnotu . 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. IOrderedFilterOrder 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:

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 CreateInstanceIFilterMetadata 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ů:

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í:

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í Resultse 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í IAsyncActionFilterActionExecutionDelegate:

  • 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:

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ů:

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 truehodnotu . 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.Canceltrue 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 CreateInstanceIFilterMetadata 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 Addobjektu , místo jeho Type, je filtr singleton a neníbezpečný pro přístup z více vláken.

Další prostředky

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:

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

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:

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

Razor 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, nextkterý 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:

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:

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.
  • 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
1 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ý se next spustí po filtrech akce.

Následující ControllerFiltersController třída:

  • SampleActionFilterAttribute Použije ([SampleActionFilter]) na kontroler.
  • Přepsání OnActionExecuting a OnActionExecuted.
[SampleActionFilter]
public class ControllerFiltersController : Controller
{
    public override void OnActionExecuting(ActionExecutingContext context)
    {
        Console.WriteLine(
            $"- {nameof(ControllerFiltersController)}.{nameof(OnActionExecuting)}");

        base.OnActionExecuting(context);
    }

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

        base.OnActionExecuted(context);
    }

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

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

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.MinValuehodnotu . 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. IOrderedFilterOrder 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:

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 CreateInstanceIFilterMetadata 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ů:

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í:

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í Resultse 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í IAsyncActionFilterActionExecutionDelegate:

  • 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:

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ů:

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 truehodnotu . 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.Canceltrue 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 CreateInstanceIFilterMetadata 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 Addobjektu , místo jeho Type, je filtr singleton a neníbezpečný pro přístup z více vláken.

Další prostředky

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.

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

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.

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

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 SampleAsyncActionFilterActionExecutionDelegate (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:

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 MyActionFilterAttributeIndex2 :

[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 AddHeaderAttributeRazor 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:

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.
  • 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
1 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 po next 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í.

The TestController:

  • SampleActionFilterAttribute Použije ([SampleActionFilter]) na FilterTest2 akci.
  • Přepsání OnActionExecuting a OnActionExecuted.
public class TestController : Controller
{
    [SampleActionFilter(Order = int.MinValue)]
    public IActionResult FilterTest2()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }

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

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

MyDisplayRouteInfo 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.MinValuehodnotu . 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. IOrderedFilterOrder 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 AddHeaderSomeResource akce. The 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:

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 CreateInstanceIFilterMetadata 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ů:

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í:

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í Resultse 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í IAsyncActionFilterActionExecutionDelegate:

  • 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:

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ů:

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 truehodnotu . 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.Canceltrue 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:

  • authorRick Anderson (autor):
  • globaladdheader:Result filter added to MvcOptions.Filters
  • Vnitřní: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 CreateInstanceIFilterMetadata 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 Addobjektu , místo jeho Type, je filtr singleton a neníbezpečný pro přístup z více vláken.

Další akce