Filter in ASP.NET Core

Von Kirk Larkin, Rick Anderson, Tom Dykstra und Steve Smith

Filter ermöglichen es in ASP.NET Core, Code vor oder nach bestimmten Phasen der Anforderungsverarbeitungspipeline auszuführen.

Integrierte Filter sind für folgende Aufgaben zuständig:

  • Autorisierung (der Zugriff auf Ressourcen, für die ein Benutzer nicht autorisiert ist, wird verhindert)
  • Zwischenspeicherung von Antworten (die Anforderungspipeline wird unterbrochen, damit eine zwischengespeicherte Antwort zurückgegeben wird)

Durch die Erstellung benutzerdefinierter Filter kann mit aktionsübergreifenden Problemen umgegangen werden. Zu solchen übergreifenden Problemen gehören beispielsweise Fehlerbehandlung, Caching, Konfiguration, Autorisierung und Protokollierung. Mit Filtern lässt sich die Duplizierung von Code vermeiden. Sie können zum Beispiel die Fehlerbehandlung in einem Ausnahmefilter konsolidieren.

Dieses Dokument gilt für Razor Pages, API-Controller und Controller mit Ansichten. Filter können nicht direkt mit Razor-Komponenten verwendet werden. Ein Filter kann nur indirekt Einfluss auf eine Komponente haben, wenn Folgendes gilt:

  • Die Komponente ist in eine Seite oder Ansicht eingebettet.
  • Die Seite oder der Controller und die Ansicht verwenden den Filter.

Die Funktionsweise von Filtern

Filter werden in der ASP.NET Core-Aktionsaufrufpipeline ausgeführt, die manchmal auch als Filterpipeline bezeichnet wird. Die Filterpipeline wird ausgeführt, nachdem ASP.NET Core die auszuführende Aktion ausgewählt hat:

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.

Filtertypen

Jeder Filtertyp wird in einer anderen Phase der Filterpipeline ausgeführt:

  • Für Autorisierungsfilter gilt Folgendes:

    • Führen Sie sie zuerst aus.
    • Bestimmen Sie, ob der Benutzer für die Anforderung autorisiert ist.
    • Schließen Sie die Pipeline kurz, wenn die Anforderung nicht autorisiert ist.
  • Ressourcenfilter:

    • Werden nach der Autorisierung ausgeführt.
    • OnResourceExecuting führt Code vor dem Rest der Filterpipeline aus. Beispielsweise führt OnResourceExecuting Code vor der Modellbindung aus.
    • OnResourceExecuted führt Code aus, nachdem der Rest der Pipeline abgeschlossen wurde.
  • Für Aktionsfilter gilt Folgendes:

    • Führen Sie ihn unmittelbar vor und nach dem Aufruf einer Aktionsmethode aus.
    • Sie können die an eine Aktion übergebenen Argumente ändern.
    • Sie können das von der Aktion zurückgegebene Ergebnis ändern.
    • Sie werden in Razor Pages nicht unterstützt.
  • Für Endpunktfilter gilt:

    • Führen Sie ihn unmittelbar vor und nach dem Aufruf einer Aktionsmethode aus.
    • Sie können die an eine Aktion übergebenen Argumente ändern.
    • Sie können das von der Aktion zurückgegebene Ergebnis ändern.
    • Sie werden in Razor Pages nicht unterstützt.
    • Sie können sowohl für Aktionen als auch für routenhandlerbasierte Endpunkte aufgerufen werden.
  • Ausnahmefilter wenden globale Richtlinien auf unbehandelte Ausnahmen an, die auftreten, bevor etwas in den Antworttext geschrieben wurde.

  • Für Ergebnisfilter gilt:

    • Sie führen Code unmittelbar vor und nach der Ausführung von Aktionsergebnissen aus.
    • Sie werden nur ausgeführt, wenn die Aktionsmethode erfolgreich ausgeführt wird.
    • Sie sind hilfreich für Logik, die die Ausführung von Ansichten oder Formatierern umfasst.

Das folgende Diagramm veranschaulicht, wie die Filtertypen mit der Filterpipeline interagieren:

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 Pages unterstützen auch Razor Page-Filter, die vor und nach einem Razor Page-Handler ausgeführt werden.

Implementierung

Filter unterstützen sowohl synchrone als auch asynchrone Implementierungen über verschiedene Schnittstellendefinitionen.

Synchrone Filter werden vor und nach der jeweiligen Pipelinephase ausgeführt. OnActionExecuting beispielsweise wird aufgerufen, bevor die Aktionsmethode aufgerufen wird. OnActionExecuted wird nach der Rückgabe der Aktionsmethode aufgerufen:

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.
    }
}

Asynchrone Filter definieren eine On-Stage-ExecutionAsync-Methode. Beispiel: 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.
    }
}

Im oben stehenden Code weist der SampleAsyncActionFilter einen ActionExecutionDelegate (next) auf, der die Aktionsmethode ausführt.

Mehrere Filterphasen

Schnittstellen für mehrere Filterphasen können in einer einzigen Klasse implementiert werden. Beispielsweise implementiert die ActionFilterAttribute-Klasse:

Implementieren Sie entweder die synchrone oder asynchrone Version einer Filterschnittstelle, aber nicht beide. Die Runtime prüft zuerst, ob der Filter die asynchrone Schnittstelle implementiert, und wenn dies der Fall ist, ruft die Runtime diese Schnittstelle auf. Wenn dies nicht der Fall ist, ruft es die Methode(n) der synchronen Schnittstelle auf. Wenn die asynchrone und die synchrone Schnittstelle in einer einzigen Klasse implementiert sind, wird nur die asynchrone Methode aufgerufen. Bei Verwendung von abstrakten Klassen wie ActionFilterAttribute überschreiben Sie nur die synchronen Methoden oder die asynchrone Methode für jeden Filtertyp.

Integrierte Filterattribute

ASP.NET Core enthält integrierte attributbasierte Filter, die als Unterklasse erstellt und angepasst werden können. Zum Beispiel fügt der folgende Ergebnisfilter der Antwort einen Header hinzu:

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);
    }
}

Wie im obigen Beispiel gezeigt, können Filter mithilfe von Attributen Argumente akzeptieren. Wenden Sie das ResponseHeaderAttribute auf einen Controller oder eine Aktionsmethode an, und geben Sie den Namen und den Wert des HTTP-Headers an:

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

    // ...

Verwenden Sie ein Tool wie die Browser-Entwicklertools, um die Header zu untersuchen. Unter Antwortheader wird filter-header: Filter Value angezeigt.

Der folgende Code wendet ResponseHeaderAttribute sowohl auf einen Controller als auch eine Aktion an:

[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.");
}

Die Antworten der Multiple-Aktion umfassen die folgenden Header:

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

Einige der Filterschnittstellen besitzen entsprechende Attribute, die als Basisklassen für benutzerdefinierte Implementierungen verwendet werden können.

Filterattribute:

Filter können nicht auf Razor Page-Handlermethoden angewendet werden. Sie können entweder auf das Razor Page-Modell oder global angewendet werden.

Filterbereiche und Reihenfolge der Ausführung

Der Pipeline kann in einem von drei Bereichen ein Filter hinzugefügt werden:

  • Verwenden eines Attributs für einen Controller oder eine Razor-Seite
  • Verwenden eines Attributs für eine Controller-Aktion. Es können keine Filterattribute auf Handlermethoden von Razor Pages angewendet werden.
  • Global für alle Controller und Aktionen, Razor Pages wie im folgenden Code gezeigt:
    var builder = WebApplication.CreateBuilder(args);
    
    // Add services to the container.
    builder.Services.AddControllersWithViews(options =>
    {
        options.Filters.Add<GlobalSampleActionFilter>();
    });
    

Standardreihenfolge der Ausführung

Wenn es mehrere Filter für eine bestimmte Stufe der Pipeline gibt, bestimmt der Bereich die Standardreihenfolge der Filterausführung. Globale Filter umfassen Klassenfilter, welche hingegen Methodenfilter umfassen.

Als Ergebnis der Filterschachtelung wird der nach einer Pipelinephase auszuführende Code in umgekehrter Reihenfolge zum Code vor einer Pipelinephase ausgeführt. Filterreihenfolge:

  • Der Code von globalen Filtern, der vor einer Pipelinephase ausgeführt werden soll
    • Der Code von Controllerfiltern, der vor einer Pipelinephase ausgeführt werden soll
      • Der Code von Aktionsmethodenfiltern, der vor einer Pipelinephase ausgeführt werden soll
      • Der Code von Aktionsmethodenfiltern, der nach einer Pipelinephase ausgeführt werden soll
    • Der Code von Controllerfiltern, der nach einer Pipelinephase ausgeführt werden soll
  • Der Code von globalen Filtern, der nach einer Pipelinephase ausgeführt werden soll

Das folgende Beispiel veranschaulicht die Reihenfolge, in der Filtermethoden für synchrone Aktionsfilter ausgeführt werden:

Sequence Filterbereich Filtermethode
1 Global OnActionExecuting
2 Controller OnActionExecuting
3 Aktion OnActionExecuting
4 Aktion OnActionExecuted
5 Controller OnActionExecuted
6 Global OnActionExecuted

Filter auf Controllerebene

Jeder Controller, der vom Controller erbt, enthält die Methoden OnActionExecuting, OnActionExecutionAsync und OnActionExecuted. Diese Methode umschließen die Filter, die für eine bestimmte Aktion ausgeführt werden:

  • OnActionExecuting wird vor allen Filtern der Aktion ausgeführt.
  • OnActionExecuted wird nach allen Filtern der Aktion ausgeführt.
  • OnActionExecutionAsync wird vor allen Filtern der Aktion ausgeführt. Der Code nach einem Aufruf von next wird nach den Filtern der Aktion ausgeführt.

Für die folgende ControllerFiltersController-Klasse gilt:

  • Sie wendet das SampleActionFilterAttribute ([SampleActionFilter]) auf den Controller an.
  • Überschreibt OnActionExecuting und 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.");
    }
}

Durch Navigieren zu https://localhost:<port>/ControllerFilters wird der folgende Code ausgeführt:

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

Filter auf Controllerebene legen die Eigenschaft Order auf int.MinValue fest. Für Filter auf Controllerebene kann die Ausführung nach Filtern, die auf Methoden angewendet werden nicht festgelegt werden. Die Reihenfolge wird im nächsten Abschnitt erläutert.

Weitere Informationen zu Razor Pages finden Sie unter Implementieren von Razor Page-Filtern durch das Überschreiben von Filtermethoden.

Überschreiben der Standardreihenfolge

Die Standardreihenfolge der Ausführung kann durch Implementieren von IOrderedFilter überschrieben werden. IOrderedFilter macht die Eigenschaft Order verfügbar, die bei der Bestimmung der Ausführungsreihenfolge Vorrang vor dem Bereich hat. Für einen Filter mit geringerem Order-Wert gilt:

  • Führt den Code, der vor einer Pipelinephase ausgeführt werden soll, vor dem Code eines Filters mit einem höheren Wert für Order aus.
  • Führt den Code, der nach einer Pipelinephase ausgeführt werden soll, nach dem Code eines Filters mit einem höheren Wert für Order aus.

Im Beispiel Filter auf Kontrollebene hat GlobalSampleActionFilter globalen Umfang und wird daher vor SampleActionFilterAttribute ausgeführt, der den Umfang des Controllers aufweist. Um SampleActionFilterAttribute zuerst ausführen zu lassen, legen Sie die Reihenfolge auf int.MinValue fest:

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

Um zuerst den globalen Filter GlobalSampleActionFilter ausführen zu lassen, legen Sie seine Order auf int.MinValue fest:

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

Abbrechen und Kurzschließen

Die Filterpipeline kann kurzgeschlossen werden, indem die Eigenschaft Result auf den Parameter ResourceExecutingContext festgelegt wird, der in der Filtermethode angegeben ist. Zum Beispiel verhindert der folgende Ressourcenfilter, dass der Rest der Pipeline ausgeführt wird:

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

    public void OnResourceExecuted(ResourceExecutedContext context) { }
}

Im folgenden Code verwenden sowohl der Filter [ShortCircuitingResourceFilter] und der Filter [ResponseHeader] die Aktionsmethode Index als Ziel. Für den ShortCircuitingResourceFilterAttribute-Filter gilt:

  • Der Filter wird zuerst ausgeführt, da es sich um einen Ressourcenfilter handelt und ResponseHeaderAttribute ein Aktionsfilter ist.
  • Der Filter unterbricht alle verbleibenden Pipelineschritte.

Der ResponseHeaderAttribute-Filter wird daher nie für die Index-Aktion ausgeführt. Dasselbe Verhalten würde auch dann auftreten, wenn beide Filter auf Aktionsmethodenebene angewendet würden, wenn ShortCircuitingResourceFilterAttribute zuerst ausgeführt wird. Das ShortCircuitingResourceFilterAttribute wird aufgrund des Filtertyps zuerst ausgeführt:

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

Abhängigkeitsinjektion

Filter können nach Typ oder Instanz hinzugefügt werden. Wenn eine Instanz hinzugefügt wird, wird diese Instanz für jede Anforderung verwendet. Wenn ein Typ hinzugefügt wird, ist der Filter typaktiviert. Ein typaktivierter Filter bedeutet Folgendes:

  • Für jede Anforderung wird eine Instanz erstellt.
  • Alle Konstruktorabhängigkeiten werden durch Dependency Injection (DI) aufgefüllt.

Filter, die als Attribute implementiert und Controllerklassen oder Aktionsmethoden direkt hinzugefügt werden, können keine Konstruktorabhängigkeiten von Dependency Injection (DI) erhalten. Konstruktorabhängigkeiten können von DI nicht bereitgestellt werden, da die Konstruktorparameter für Attribute dort bereitgestellt werden müssen, wo sie angewendet werden.

Die folgenden Filter unterstützen von DI bereitgestellte Konstruktorabhängigkeiten:

Die oben genannten Filter können auf einen Controller oder eine Aktion angewendet werden.

DI bietet Protokollierungen. Vermeiden Sie es jedoch, Filter nur zum Zweck der Protokollierung zu erstellen und zu verwenden. Die integrierte Frameworkprotokollierung bietet in der Regel alle Elemente, die für die Protokollierung erforderlich sind. Wenn Protokollierung zu Filtern hinzugefügt wird, gilt Folgendes:

  • Die Protokollierung sollte mit Schwerpunkt auf geschäftlichen Aspekten oder verhaltensspezifisch für den Filter erfolgen.
  • Aktionen oder andere Frameworkereignisse sollten nicht protokolliert werden. Die integrierten Filter protokollieren Aktionen und Frameworkereignisse bereits.

ServiceFilterAttribute

Die Typen der Dienstfilterimplementierung werden in Program.cs registriert. Ein ServiceFilterAttribute ruft eine Instanz des Filter von DI ab.

Der folgende Code zeigt die LoggingResponseHeaderFilterService-Klasse, die DI verwendet:

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)}");
    }
}

Im folgenden Code wird LoggingResponseHeaderFilterService zum DI-Container hinzugefügt:

builder.Services.AddScoped<LoggingResponseHeaderFilterService>();

Im folgenden Code ruft das ServiceFilter-Attribut eine Instanz des LoggingResponseHeaderFilterService-Filters aus DI ab:

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

Wenn Sie ServiceFilterAttribute verwenden, bewirkt das Festlegen von ServiceFilterAttribute.IsReusable Folgendes:

  • Gibt einen Hinweis darauf, dass die Filterinstanz möglicherweise außerhalb des Anforderungsbereichs, in dem sie erstellt wurde, wiederverwendet wird. Die ASP.NET Core-Runtime bietet keine Garantie für Folgendes:
    • Eine einzelne Instanz des Filters wird erstellt.
    • Der Filter wird nicht zu einem späteren Zeitpunkt vom DI-Container erneut angefordert.
  • Das Attribut sollte nicht mit einem Filter verwendet werden, der von Diensten mit einer anderen Lebensdauer als der eines Singletons abhängig ist.

ServiceFilterAttribute implementiert IFilterFactory. IFilterFactory macht die CreateInstance-Methode zum Erstellen einer IFilterMetadata-Instanz verfügbar. CreateInstance lädt den angegebenen Typ aus DI.

TypeFilterAttribute

TypeFilterAttribute ähnelt ServiceFilterAttribute. Der zugehörige Typ wird allerdings nicht direkt vom DI-Container aufgelöst. Stattdessen wird der Typ mithilfe von Microsoft.Extensions.DependencyInjection.ObjectFactory instanziiert.

Da TypeFilterAttribute-Typen nicht direkt vom DI-Container aufgelöst werden, gilt Folgendes:

  • Typen, auf die mithilfe von TypeFilterAttribute verwiesen wird, müssen nicht beim DI-Container registriert werden. Ihre Abhängigkeiten werden vom DI-Container erfüllt.
  • TypeFilterAttribute kann optional Konstruktorargumente für den Typ akzeptieren.

Wenn Sie TypeFilterAttribute verwenden, bewirkt das Festlegen von TypeFilterAttribute.IsReusable Folgendes:

  • Gibt einen Hinweis darauf, dass die Filterinstanz möglicherweise außerhalb des Anforderungsbereichs wiederverwendet wird, in dem sie erstellt wurde. Die ASP.NET Core-Runtime bietet keine Garantie dafür, dass eine einzelne Instanz des Filters erstellt wird.

  • Sollte nicht mit einem Filter verwendet werden, der von Diensten mit einer anderen Lebensdauer als der eines Singletons abhängig ist

Das folgende Beispiel zeigt, wie Sie Argumente durch Verwendung von TypeFilterAttribute an einen Typ übergeben:

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

Autorisierungsfilter

Für Autorisierungsfilter gilt Folgendes:

  • Sie werden als erste Filter in der Filterpipeline ausgeführt.
  • Sie steuern den Zugriff auf Aktionsmethoden.
  • Sie verfügen nur über eine Methode, die vor der Aktion ausgeführt wird, nicht jedoch über eine Methode, die nach der Aktion ausgeführt wird.

Benutzerdefinierte Autorisierungsfilter erfordern ein benutzerdefiniertes Autorisierungsframework. Statt einen benutzerdefinierten Filter zu schreiben, sollten Sie die Autorisierungsrichtlinien konfigurieren oder eine benutzerdefinierte Autorisierungsrichtlinie schreiben. Für den integrierten Autorisierungsfilter gilt:

  • Er ruft das Autorisierungssystem auf.
  • Er autorisiert keine Anforderungen.

Lösen Sie keine Ausnahmen in Autorisierungsfiltern aus:

  • Die Ausnahme wird nicht behandelt.
  • Ausnahmefilter behandeln die Ausnahme nicht.

Beim Auftreten einer Ausnahme in einem Autorisierungsfilter sollten Sie eine Abfrage erwägen.

Weitere Informationen finden Sie unter Autorisierung.

Ressourcenfilter

Ressourcenfilter:

Ressourcenfilter sind hilfreich, um den größten Teil der Pipeline kurzzuschließen. Ein Cachingfilter kann beispielsweise bei einem Cachetreffer den Rest der Pipeline überspringen.

Beispiele für Ressourcenfilter:

Aktionsfilter

Aktionsfilter gelten nicht für Razor Pages. Razor Pages unterstützt IPageFilter und IAsyncPageFilter. Weitere Informationen finden Sie unter Filtermethoden für Razor Pages.

Für Aktionsfilter gilt Folgendes:

Der folgende Code zeigt einen Beispielaktionsfilter:

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.
    }
}

ActionExecutingContext bietet folgende Eigenschaften:

  • ActionArguments ermöglicht das Lesen der Eingaben für eine Aktionsmethode.
  • Controller ermöglicht das Ändern der Controllerinstanz.
  • Result: Die Festlegung von Result schließt die Ausführung der Aktionsmethode und der nachfolgenden Aktionsfilter kurz.

Durch Auslösen einer Ausnahme in einer Aktionsmethode geschieht Folgendes:

  • Die Ausführung nachfolgender Filter wird verhindert.
  • Im Gegensatz zum Festlegen von Result wird die ausgelöste Ausnahme wird nicht als erfolgreiches Ergebnis, sondern als Fehler behandelt.

ActionExecutedContext bietet Controller und Result sowie die folgenden Eigenschaften:

  • Canceled ist TRUE, wenn die Aktionsausführung durch einen anderen Filter kurzgeschlossen wurde.
  • Exception ist ungleich NULL, wenn die Aktion oder ein zuvor ausgeführter Aktionsfilter eine Ausnahme ausgelöst hat. Durch Festlegen dieser Eigenschaft auf NULL geschieht Folgendes:
    • Die Ausnahme wird effektiv behandelt.
    • Result wird so ausgeführt, als wäre das Ergebnis von der Aktionsmethode zurückgegeben worden.

Bei einem IAsyncActionFilter hat der Aufruf von ActionExecutionDelegate folgende Auswirkungen:

  • Alle nachfolgenden Aktionsfilter und die Aktionsmethode werden ausgeführt.
  • Gibt ActionExecutedContext zurück.

Weisen Sie einer Ergebnisinstanz Microsoft.AspNetCore.Mvc.Filters.ActionExecutingContext.Result zu, und rufen Sie nicht next (den ActionExecutionDelegate) auf, um die Pipeline kurzzuschließen.

Das Framework stellt ein abstraktes ActionFilterAttribute bereit, das als Unterklasse verwendet werden kann.

Der Aktionsfilter OnActionExecuting kann für Folgendes verwendet werden:

  • Überprüfen des Modellzustands.
  • Zurückgeben eines Fehlers, wenn der Zustand ungültig ist.
public class ValidateModelAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext context)
    {
        if (!context.ModelState.IsValid)
        {
            context.Result = new BadRequestObjectResult(context.ModelState);
        }
    }
}

Hinweis

Controller, die mit dem [ApiController]-Attribut kommentiert werden, überprüfen automatisch den Modellzustand und geben eine 400-Antwort zurück. Weitere Informationen finden Sie unter Automatische HTTP 400-Antworten.

Die Methode OnActionExecuted wird nach der Aktionsmethode ausgeführt. Es gilt Folgendes:

  • Die Methode kann die Ergebnisse der Aktion durch die Eigenschaft Result sehen und ändern.
  • Canceled wird auf TRUE festgelegt, wenn die Ausführung der Aktion durch einen anderen Filter kurzgeschlossen wurde.
  • Exception wird auf einen Wert ungleich NULL festgelegt, wenn die Aktion oder ein nachfolgender Aktionsfilter eine Ausnahme ausgelöst hat. Wenn für Exception NULL festgelegt wird,
    • werden Ausnahmen effektiv behandelt,
    • und ActionExecutedContext.Result wird so ausgeführt, als würde die Eigenschaft normal von der Aktionsmethode zurückgegeben werden.

Ausnahmefilter

Für Ausnahmefilter gilt Folgendes:

Der folgende Beispielausnahmefilter zeigt Details zu Ausnahmen an, die auftreten, wenn die App sich in der Entwicklung befindet:

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()
        };
    }
}

Mit dem folgenden Code wird der Ausnahmefilter getestet:

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

Für Ausnahmefilter gilt Folgendes:

  • Sie verfügen nicht über vorangehende oder darauffolgende Ereignisse.
  • Sie implementieren OnException oder OnExceptionAsync.
  • Sie behandeln Ausnahmefehler, die bei der Erstellung von Razor Pages oder Controllern, bei der Modellbindung, bei Aktionsfiltern oder bei Aktionsmethoden auftreten.
  • Sie erfassen keine Ausnahmen, die in Ressourcenfiltern, Ergebnisfiltern oder bei der Ausführung von MVC-Ergebnissen auftreten.

Um eine Ausnahme zu behandeln, legen Sie die Eigenschaft ExceptionHandled auf true fest, oder weisen Sie die Eigenschaft Result zu. Dadurch wird die Weitergabe der Ausnahme beendet. Ein Ausnahmefilter kann eine Ausnahme nicht in ein „erfolgreiches Ergebnis“ umwandeln. Nur ein Aktionsfilter kann das.

Für Ausnahmefilter gilt Folgendes:

  • Sie eignen sich für das Abfangen von Ausnahmen, die in Aktionen auftreten.
  • Sie sind im Vergleich zu Middleware für die Fehlerbehandlung weniger flexibel.

Sie sollten bei der Ausnahmebehandlung vorzugsweise Middleware verwenden. Verwenden Sie Ausnahmefilter nur, wenn sich die Fehlerbehandlung unterscheidet, je nachdem, welche Aktionsmethode aufgerufen wird. Zum Beispiel verfügt eine App möglicherweise über Aktionsmethoden für beide API-Endpunkte und für Ansichten bzw. HTML. Die API-Endpunkte können Fehlerinformationen als JSON zurückgeben, während ansichtsbasierte Aktionen eine Fehlerseite als HTML zurückgeben können.

Ergebnisfilter

Für Ergebnisfilter gilt:

IResultFilter und IAsyncResultFilter

Der folgende Code zeigt einen Beispielergebnisfilter:

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.
    }
}

Die Art des Ergebnisses, das ausgeführt wird, hängt von der jeweiligen Aktion ab. Eine Aktion, die eine Ansicht zurückgibt, beinhaltet die gesamte Razor-Verarbeitung als Teil der Ausführung von ViewResult. Eine API-Methode führt möglicherweise eine Art Serialisierung als Teil der Ausführung des Ergebnisses durch. Erfahren Sie mehr zu Aktionsergebnissen.

Ergebnisfilter werden nur ausgeführt, wenn eine Aktion oder ein Aktionsfilter ein Aktionsergebnis liefert. Ergebnisfilter werden in folgenden Fällen nicht ausgeführt:

  • Ein Autorisierungs- oder Ressourcenfilter schließt die Pipeline kurz.
  • Ein Ausnahmefilter behandelt eine Ausnahme durch Erzeugen eines Aktionsergebnisses.

Die Methode Microsoft.AspNetCore.Mvc.Filters.IResultFilter.OnResultExecuting kann die Ausführung des Aktionsergebnisses und der nachfolgenden Ergebnisfilter durch Festlegen von Microsoft.AspNetCore.Mvc.Filters.ResultExecutingContext.Cancel auf true kurzschließen. Schreiben Sie beim Kurzschließen in das Antwortobjekt, um zu verhindern, dass eine leere Antwort generiert wird. Das Auslösen einer Ausnahme in IResultFilter.OnResultExecuting bewirkt Folgendes:

  • Die Ausführung des Aktionsergebnisses und der nachfolgenden Filter wird verhindert.
  • Die ausgelöste Ausnahme wird nicht als erfolgreiches Ergebnis, sondern als Fehler behandelt.

Wenn die Microsoft.AspNetCore.Mvc.Filters.IResultFilter.OnResultExecuted-Methode ausgeführt wird, wurde die Antwort wahrscheinlich bereits an den Client gesendet. Wenn die Antwort bereits an den Client gesendet wurde, kann sie nicht geändert werden.

ResultExecutedContext.Canceled wird auf true festgelegt, wenn die Ausführung des Aktionsergebnisses durch einen anderen Filter kurzgeschlossen wurde.

ResultExecutedContext.Exception wird auf einen Wert ungleich NULL festgelegt, wenn das Aktionsergebnis oder ein nachfolgender Ergebnisfilter eine Ausnahme ausgelöst hat. Das Festlegen von Exception auf NULL behandelt eine Ausnahme und verhindert, dass die Ausnahme zu einem späteren Zeitpunkt in der Pipeline ein weiteres Mal ausgelöst wird. Beim Behandeln einer Ausnahme in einem Ergebnisfilter gibt es keine zuverlässige Möglichkeit, Daten in eine Antwort zu schreiben. Wenn ein Aktionsergebnis eine Ausnahme auslöst und die Header bereits an den Client übergeben wurden, gibt es keinen zuverlässigen Mechanismus, einen Fehlercode zu senden.

Bei einem IAsyncResultFilter führt ein Aufruf von await next im ResultExecutionDelegate alle nachfolgenden Ergebnisfilter und das Aktionsergebnis aus. Legen Sie ResultExecutingContext.Cancel auf true fest, und rufen Sie nicht ResultExecutionDelegate auf, um die Pipeline kurzzuschließen.

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

Das Framework stellt ein abstraktes ResultFilterAttribute bereit, das als Unterklasse verwendet werden kann. Die weiter oben gezeigte Klasse ResponseHeaderAttribute ist ein Beispiel für ein Ergebnisfilterattribut.

IAlwaysRunResultFilter und IAsyncAlwaysRunResultFilter

Die Schnittstellen IAlwaysRunResultFilter und IAsyncAlwaysRunResultFilter deklarieren eine IResultFilter-Implementierung, die für alle Aktionsergebnisse ausgeführt wird. Dies schließt Aktionsergebnisse ein, die von folgenden Filtern generiert werden:

  • Autorisierungs- und Ressourcenfilter, die einen Kurzschluss bewirken
  • Ausnahmefilter

Beispielsweise wird der folgende Filter immer ausgeführt und legt ein Aktionsergebnis (ObjectResult) mit dem Statuscode 422 – Einheit kann nicht bearbeitet werden fest, wenn bei der Inhaltsaushandlung ein Fehler auftritt:

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 implementiert IFilterMetadata. Deshalb kann eine IFilterFactory-Instanz an einer beliebigen Stelle in der Filterpipeline als IFilterMetadata-Instanz verwendet werden. Wenn die Runtime den Aufruf des Filters vorbereitet, versucht sie, ihn in eine IFilterFactory umzuwandeln. Wenn diese Umwandlung gelingt, wird die Methode CreateInstance aufgerufen, um die Instanz IFilterMetadata für den Aufruf zu erstellen. Da die exakte Filterpipeline beim Start der Anwendung nicht explizit festgelegt werden muss, wird dadurch ein sehr flexibles Design ermöglicht.

IFilterFactory.IsReusable:

  • Ist ein Hinweis der Factory darauf, dass die von der Factory erstellte Filterinstanz möglicherweise außerhalb des Anforderungsbereichs, in dem sie erstellt wurde, wiederverwendet wird
  • Sollte nicht mit einem Filter verwendet werden, der von Diensten mit einer anderen Lebensdauer als der eines Singletons abhängig ist

Die ASP.NET Core-Runtime bietet keine Garantie für Folgendes:

  • Eine einzelne Instanz des Filters wird erstellt.
  • Der Filter wird nicht zu einem späteren Zeitpunkt vom DI-Container erneut angefordert.

Warnung

Konfigurieren Sie IFilterFactory.IsReusable nur so, dass true zurückgegeben wird, wenn die Quelle der Filter eindeutig ist, die Filter zustandslos sind und die Filter sicher in mehreren HTTP-Anforderungen verwendet werden können. Geben Sie beispielsweise keine Filter aus DI zurück, die als bereichsbezogen oder vorübergehend registriert sind, wenn IFilterFactory.IsReusabletrue zurückgibt.

Als weiteres Verfahren zum Erstellen von Filtern kann IFilterFactory mithilfe von benutzerdefinierten Attributimplementierungen implementiert werden:

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) { }
    }

Der Filter wird im folgenden Code angewendet:

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

In einem Attribut implementierte IFilterFactory

Filter, die IFilterFactory implementieren, eignen sich in folgenden Fällen:

  • Sie erfordern kein Übergeben von Parametern.
  • Sie verfügen über Konstruktorabhängigkeiten, die durch DI erfüllt werden müssen.

TypeFilterAttribute implementiert IFilterFactory. IFilterFactory macht die CreateInstance-Methode zum Erstellen einer IFilterMetadata-Instanz verfügbar. CreateInstance lädt den angegebenen Typ aus dem Dienstecontainer (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)}");
        }
    }
}

Der folgende Code zeigt drei Ansätze zum Anwenden des Filters:

[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)}");

Im obigen Code wird der erste Ansatz zum Anwenden des Filters bevorzugt.

Verwenden von Middleware in der Filterpipeline

Ressourcenfilter funktionieren wie Middleware, sie umschließen alle Ausführungen, die später in der Pipeline enthalten sind. Filter unterscheiden sich jedoch insofern von Middleware, dass sie Teil der Runtime sind, was bedeutet, dass sie Zugriff auf Kontext und Konstrukte haben.

Um Middleware als Filter zu verwenden, erstellen Sie einen Typ mit einer Configure-Methode, die die Middleware festlegt, die in die Filterpipeline eingefügt werden soll. Im folgenden Beispiel wird Middleware verwendet, um einen Antwortheader festzulegen:

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

            await next();
        });
    }
}

Verwenden Sie das MiddlewareFilterAttribute zum Ausführen der Middleware:

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

Middlewarefilter werden zum selben Zeitpunkt in der Filterpipeline wie Ressourcenfilter, vor der Modellbindung und nach dem Rest der Pipeline ausgeführt.

Threadsicherheit

Wenn eine Instanz eines Filters in Add statt in Type übergeben wird, ist der Filter ein Singleton und ist nicht threadsicher.

Zusätzliche Ressourcen

Von Kirk Larkin, Rick Anderson, Tom Dykstra und Steve Smith

Filter ermöglichen es in ASP.NET Core, Code vor oder nach bestimmten Phasen der Anforderungsverarbeitungspipeline auszuführen.

Integrierte Filter sind für folgende Aufgaben zuständig:

  • Autorisierung (der Zugriff auf Ressourcen, für die ein Benutzer nicht autorisiert ist, wird verhindert)
  • Zwischenspeicherung von Antworten (die Anforderungspipeline wird unterbrochen, damit eine zwischengespeicherte Antwort zurückgegeben wird)

Durch die Erstellung benutzerdefinierter Filter kann mit aktionsübergreifenden Problemen umgegangen werden. Zu solchen übergreifenden Problemen gehören beispielsweise Fehlerbehandlung, Caching, Konfiguration, Autorisierung und Protokollierung. Mit Filtern lässt sich die Duplizierung von Code vermeiden. Sie können zum Beispiel die Fehlerbehandlung in einem Ausnahmefilter konsolidieren.

Dieses Dokument gilt für Razor Pages, API-Controller und Controller mit Ansichten. Filter können nicht direkt mit Razor-Komponenten verwendet werden. Ein Filter kann nur indirekt Einfluss auf eine Komponente haben, wenn Folgendes gilt:

  • Die Komponente ist in eine Seite oder Ansicht eingebettet.
  • Die Seite oder der Controller und die Ansicht verwenden den Filter.

Die Funktionsweise von Filtern

Filter werden in der ASP.NET Core-Aktionsaufrufpipeline ausgeführt, die manchmal auch als Filterpipeline bezeichnet wird. Die Filterpipeline wird ausgeführt, nachdem ASP.NET Core die auszuführende Aktion ausgewählt hat:

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.

Filtertypen

Jeder Filtertyp wird in einer anderen Phase der Filterpipeline ausgeführt:

  • Für Autorisierungsfilter gilt Folgendes:

    • Führen Sie sie zuerst aus.
    • Bestimmen Sie, ob der Benutzer für die Anforderung autorisiert ist.
    • Schließen Sie die Pipeline kurz, wenn die Anforderung nicht autorisiert ist.
  • Ressourcenfilter:

    • Werden nach der Autorisierung ausgeführt.
    • OnResourceExecuting führt Code vor dem Rest der Filterpipeline aus. Beispielsweise führt OnResourceExecuting Code vor der Modellbindung aus.
    • OnResourceExecuted führt Code aus, nachdem der Rest der Pipeline abgeschlossen wurde.
  • Für Aktionsfilter gilt Folgendes:

    • Führen Sie ihn unmittelbar vor und nach dem Aufruf einer Aktionsmethode aus.
    • Sie können die an eine Aktion übergebenen Argumente ändern.
    • Sie können das von der Aktion zurückgegebene Ergebnis ändern.
    • Sie werden in Razor Pages nicht unterstützt.
  • Für Endpunktfilter gilt:

    • Führen Sie ihn unmittelbar vor und nach dem Aufruf einer Aktionsmethode aus.
    • Sie können die an eine Aktion übergebenen Argumente ändern.
    • Sie können das von der Aktion zurückgegebene Ergebnis ändern.
    • Sie werden in Razor Pages nicht unterstützt.
    • Sie können sowohl für Aktionen als auch für routenhandlerbasierte Endpunkte aufgerufen werden.
  • Ausnahmefilter wenden globale Richtlinien auf unbehandelte Ausnahmen an, die auftreten, bevor etwas in den Antworttext geschrieben wurde.

  • Für Ergebnisfilter gilt:

    • Sie führen Code unmittelbar vor und nach der Ausführung von Aktionsergebnissen aus.
    • Sie werden nur ausgeführt, wenn die Aktionsmethode erfolgreich ausgeführt wird.
    • Sie sind hilfreich für Logik, die die Ausführung von Ansichten oder Formatierern umfasst.

Das folgende Diagramm veranschaulicht, wie die Filtertypen mit der Filterpipeline interagieren:

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 Pages unterstützen auch Razor Page-Filter, die vor und nach einem Razor Page-Handler ausgeführt werden.

Implementierung

Filter unterstützen sowohl synchrone als auch asynchrone Implementierungen über verschiedene Schnittstellendefinitionen.

Synchrone Filter werden vor und nach der jeweiligen Pipelinephase ausgeführt. OnActionExecuting beispielsweise wird aufgerufen, bevor die Aktionsmethode aufgerufen wird. OnActionExecuted wird nach der Rückgabe der Aktionsmethode aufgerufen:

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.
    }
}

Asynchrone Filter definieren eine On-Stage-ExecutionAsync-Methode. Beispiel: 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.
    }
}

Im oben stehenden Code weist der SampleAsyncActionFilter einen ActionExecutionDelegate (next) auf, der die Aktionsmethode ausführt.

Mehrere Filterphasen

Schnittstellen für mehrere Filterphasen können in einer einzigen Klasse implementiert werden. Beispielsweise implementiert die ActionFilterAttribute-Klasse:

Implementieren Sie entweder die synchrone oder asynchrone Version einer Filterschnittstelle, aber nicht beide. Die Runtime prüft zuerst, ob der Filter die asynchrone Schnittstelle implementiert, und wenn dies der Fall ist, ruft die Runtime diese Schnittstelle auf. Wenn dies nicht der Fall ist, ruft es die Methode(n) der synchronen Schnittstelle auf. Wenn die asynchrone und die synchrone Schnittstelle in einer einzigen Klasse implementiert sind, wird nur die asynchrone Methode aufgerufen. Bei Verwendung von abstrakten Klassen wie ActionFilterAttribute überschreiben Sie nur die synchronen Methoden oder die asynchrone Methode für jeden Filtertyp.

Integrierte Filterattribute

ASP.NET Core enthält integrierte attributbasierte Filter, die als Unterklasse erstellt und angepasst werden können. Zum Beispiel fügt der folgende Ergebnisfilter der Antwort einen Header hinzu:

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);
    }
}

Wie im obigen Beispiel gezeigt, können Filter mithilfe von Attributen Argumente akzeptieren. Wenden Sie das ResponseHeaderAttribute auf einen Controller oder eine Aktionsmethode an, und geben Sie den Namen und den Wert des HTTP-Headers an:

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

    // ...

Verwenden Sie ein Tool wie die Browser-Entwicklertools, um die Header zu untersuchen. Unter Antwortheader wird filter-header: Filter Value angezeigt.

Der folgende Code wendet ResponseHeaderAttribute sowohl auf einen Controller als auch eine Aktion an:

[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.");
}

Die Antworten der Multiple-Aktion umfassen die folgenden Header:

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

Einige der Filterschnittstellen besitzen entsprechende Attribute, die als Basisklassen für benutzerdefinierte Implementierungen verwendet werden können.

Filterattribute:

Filter können nicht auf Razor Page-Handlermethoden angewendet werden. Sie können entweder auf das Razor Page-Modell oder global angewendet werden.

Filterbereiche und Reihenfolge der Ausführung

Der Pipeline kann in einem von drei Bereichen ein Filter hinzugefügt werden:

  • Verwenden eines Attributs für einen Controller oder eine Razor-Seite
  • Verwenden eines Attributs für eine Controller-Aktion. Es können keine Filterattribute auf Handlermethoden von Razor Pages angewendet werden.
  • Global für alle Controller und Aktionen, Razor Pages wie im folgenden Code gezeigt:
    var builder = WebApplication.CreateBuilder(args);
    
    // Add services to the container.
    builder.Services.AddControllersWithViews(options =>
    {
        options.Filters.Add<GlobalSampleActionFilter>();
    });
    

Standardreihenfolge der Ausführung

Wenn es mehrere Filter für eine bestimmte Stufe der Pipeline gibt, bestimmt der Bereich die Standardreihenfolge der Filterausführung. Globale Filter umfassen Klassenfilter, welche hingegen Methodenfilter umfassen.

Als Ergebnis der Filterschachtelung wird der nach einer Pipelinephase auszuführende Code in umgekehrter Reihenfolge zum Code vor einer Pipelinephase ausgeführt. Filterreihenfolge:

  • Der Code von globalen Filtern, der vor einer Pipelinephase ausgeführt werden soll
    • Der Code von Controllerfiltern, der vor einer Pipelinephase ausgeführt werden soll
      • Der Code von Aktionsmethodenfiltern, der vor einer Pipelinephase ausgeführt werden soll
      • Der Code von Aktionsmethodenfiltern, der nach einer Pipelinephase ausgeführt werden soll
    • Der Code von Controllerfiltern, der nach einer Pipelinephase ausgeführt werden soll
  • Der Code von globalen Filtern, der nach einer Pipelinephase ausgeführt werden soll

Das folgende Beispiel veranschaulicht die Reihenfolge, in der Filtermethoden für synchrone Aktionsfilter ausgeführt werden:

Sequence Filterbereich Filtermethode
1 Global OnActionExecuting
2 Controller OnActionExecuting
3 Aktion OnActionExecuting
4 Aktion OnActionExecuted
5 Controller OnActionExecuted
6 Global OnActionExecuted

Filter auf Controllerebene

Jeder Controller, der vom Controller erbt, enthält die Methoden OnActionExecuting, OnActionExecutionAsync und OnActionExecuted. Diese Methode umschließen die Filter, die für eine bestimmte Aktion ausgeführt werden:

  • OnActionExecuting wird vor allen Filtern der Aktion ausgeführt.
  • OnActionExecuted wird nach allen Filtern der Aktion ausgeführt.
  • OnActionExecutionAsync wird vor allen Filtern der Aktion ausgeführt. Der Code nach einem Aufruf von next wird nach den Filtern der Aktion ausgeführt.

Für die folgende ControllerFiltersController-Klasse gilt:

  • Sie wendet das SampleActionFilterAttribute ([SampleActionFilter]) auf den Controller an.
  • Überschreibt OnActionExecuting und 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.");
    }
}

Durch Navigieren zu https://localhost:<port>/ControllerFilters wird der folgende Code ausgeführt:

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

Filter auf Controllerebene legen die Eigenschaft Order auf int.MinValue fest. Für Filter auf Controllerebene kann die Ausführung nach Filtern, die auf Methoden angewendet werden nicht festgelegt werden. Die Reihenfolge wird im nächsten Abschnitt erläutert.

Weitere Informationen zu Razor Pages finden Sie unter Implementieren von Razor Page-Filtern durch das Überschreiben von Filtermethoden.

Überschreiben der Standardreihenfolge

Die Standardreihenfolge der Ausführung kann durch Implementieren von IOrderedFilter überschrieben werden. IOrderedFilter macht die Eigenschaft Order verfügbar, die bei der Bestimmung der Ausführungsreihenfolge Vorrang vor dem Bereich hat. Für einen Filter mit geringerem Order-Wert gilt:

  • Führt den Code, der vor einer Pipelinephase ausgeführt werden soll, vor dem Code eines Filters mit einem höheren Wert für Order aus.
  • Führt den Code, der nach einer Pipelinephase ausgeführt werden soll, nach dem Code eines Filters mit einem höheren Wert für Order aus.

Im Beispiel Filter auf Kontrollebene hat GlobalSampleActionFilter globalen Umfang und wird daher vor SampleActionFilterAttribute ausgeführt, der den Umfang des Controllers aufweist. Um SampleActionFilterAttribute zuerst ausführen zu lassen, legen Sie die Reihenfolge auf int.MinValue fest:

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

Um zuerst den globalen Filter GlobalSampleActionFilter ausführen zu lassen, legen Sie seine Order auf int.MinValue fest:

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

Abbrechen und Kurzschließen

Die Filterpipeline kann kurzgeschlossen werden, indem die Eigenschaft Result auf den Parameter ResourceExecutingContext festgelegt wird, der in der Filtermethode angegeben ist. Zum Beispiel verhindert der folgende Ressourcenfilter, dass der Rest der Pipeline ausgeführt wird:

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

    public void OnResourceExecuted(ResourceExecutedContext context) { }
}

Im folgenden Code verwenden sowohl der Filter [ShortCircuitingResourceFilter] und der Filter [ResponseHeader] die Aktionsmethode Index als Ziel. Für den ShortCircuitingResourceFilterAttribute-Filter gilt:

  • Der Filter wird zuerst ausgeführt, da es sich um einen Ressourcenfilter handelt und ResponseHeaderAttribute ein Aktionsfilter ist.
  • Der Filter unterbricht alle verbleibenden Pipelineschritte.

Der ResponseHeaderAttribute-Filter wird daher nie für die Index-Aktion ausgeführt. Dasselbe Verhalten würde auch dann auftreten, wenn beide Filter auf Aktionsmethodenebene angewendet würden, wenn ShortCircuitingResourceFilterAttribute zuerst ausgeführt wird. Das ShortCircuitingResourceFilterAttribute wird aufgrund des Filtertyps zuerst ausgeführt:

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

Abhängigkeitsinjektion

Filter können nach Typ oder Instanz hinzugefügt werden. Wenn eine Instanz hinzugefügt wird, wird diese Instanz für jede Anforderung verwendet. Wenn ein Typ hinzugefügt wird, ist der Filter typaktiviert. Ein typaktivierter Filter bedeutet Folgendes:

  • Für jede Anforderung wird eine Instanz erstellt.
  • Alle Konstruktorabhängigkeiten werden durch Dependency Injection (DI) aufgefüllt.

Filter, die als Attribute implementiert und Controllerklassen oder Aktionsmethoden direkt hinzugefügt werden, können keine Konstruktorabhängigkeiten von Dependency Injection (DI) erhalten. Konstruktorabhängigkeiten können von DI nicht bereitgestellt werden, da die Konstruktorparameter für Attribute dort bereitgestellt werden müssen, wo sie angewendet werden.

Die folgenden Filter unterstützen von DI bereitgestellte Konstruktorabhängigkeiten:

Die oben genannten Filter können auf einen Controller oder eine Aktion angewendet werden.

DI bietet Protokollierungen. Vermeiden Sie es jedoch, Filter nur zum Zweck der Protokollierung zu erstellen und zu verwenden. Die integrierte Frameworkprotokollierung bietet in der Regel alle Elemente, die für die Protokollierung erforderlich sind. Wenn Protokollierung zu Filtern hinzugefügt wird, gilt Folgendes:

  • Die Protokollierung sollte mit Schwerpunkt auf geschäftlichen Aspekten oder verhaltensspezifisch für den Filter erfolgen.
  • Aktionen oder andere Frameworkereignisse sollten nicht protokolliert werden. Die integrierten Filter protokollieren Aktionen und Frameworkereignisse bereits.

ServiceFilterAttribute

Die Typen der Dienstfilterimplementierung werden in Program.cs registriert. Ein ServiceFilterAttribute ruft eine Instanz des Filter von DI ab.

Der folgende Code zeigt die LoggingResponseHeaderFilterService-Klasse, die DI verwendet:

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)}");
    }
}

Im folgenden Code wird LoggingResponseHeaderFilterService zum DI-Container hinzugefügt:

builder.Services.AddScoped<LoggingResponseHeaderFilterService>();

Im folgenden Code ruft das ServiceFilter-Attribut eine Instanz des LoggingResponseHeaderFilterService-Filters aus DI ab:

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

Wenn Sie ServiceFilterAttribute verwenden, bewirkt das Festlegen von ServiceFilterAttribute.IsReusable Folgendes:

  • Gibt einen Hinweis darauf, dass die Filterinstanz möglicherweise außerhalb des Anforderungsbereichs, in dem sie erstellt wurde, wiederverwendet wird. Die ASP.NET Core-Runtime bietet keine Garantie für Folgendes:
    • Eine einzelne Instanz des Filters wird erstellt.
    • Der Filter wird nicht zu einem späteren Zeitpunkt vom DI-Container erneut angefordert.
  • Das Attribut sollte nicht mit einem Filter verwendet werden, der von Diensten mit einer anderen Lebensdauer als der eines Singletons abhängig ist.

ServiceFilterAttribute implementiert IFilterFactory. IFilterFactory macht die CreateInstance-Methode zum Erstellen einer IFilterMetadata-Instanz verfügbar. CreateInstance lädt den angegebenen Typ aus DI.

TypeFilterAttribute

TypeFilterAttribute ähnelt ServiceFilterAttribute. Der zugehörige Typ wird allerdings nicht direkt vom DI-Container aufgelöst. Stattdessen wird der Typ mithilfe von Microsoft.Extensions.DependencyInjection.ObjectFactory instanziiert.

Da TypeFilterAttribute-Typen nicht direkt vom DI-Container aufgelöst werden, gilt Folgendes:

  • Typen, auf die mithilfe von TypeFilterAttribute verwiesen wird, müssen nicht beim DI-Container registriert werden. Ihre Abhängigkeiten werden vom DI-Container erfüllt.
  • TypeFilterAttribute kann optional Konstruktorargumente für den Typ akzeptieren.

Wenn Sie TypeFilterAttribute verwenden, bewirkt das Festlegen von TypeFilterAttribute.IsReusable Folgendes:

  • Gibt einen Hinweis darauf, dass die Filterinstanz möglicherweise außerhalb des Anforderungsbereichs wiederverwendet wird, in dem sie erstellt wurde. Die ASP.NET Core-Runtime bietet keine Garantie dafür, dass eine einzelne Instanz des Filters erstellt wird.

  • Sollte nicht mit einem Filter verwendet werden, der von Diensten mit einer anderen Lebensdauer als der eines Singletons abhängig ist

Das folgende Beispiel zeigt, wie Sie Argumente durch Verwendung von TypeFilterAttribute an einen Typ übergeben:

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

Autorisierungsfilter

Für Autorisierungsfilter gilt Folgendes:

  • Sie werden als erste Filter in der Filterpipeline ausgeführt.
  • Sie steuern den Zugriff auf Aktionsmethoden.
  • Sie verfügen nur über eine Methode, die vor der Aktion ausgeführt wird, nicht jedoch über eine Methode, die nach der Aktion ausgeführt wird.

Benutzerdefinierte Autorisierungsfilter erfordern ein benutzerdefiniertes Autorisierungsframework. Statt einen benutzerdefinierten Filter zu schreiben, sollten Sie die Autorisierungsrichtlinien konfigurieren oder eine benutzerdefinierte Autorisierungsrichtlinie schreiben. Für den integrierten Autorisierungsfilter gilt:

  • Er ruft das Autorisierungssystem auf.
  • Er autorisiert keine Anforderungen.

Lösen Sie keine Ausnahmen in Autorisierungsfiltern aus:

  • Die Ausnahme wird nicht behandelt.
  • Ausnahmefilter behandeln die Ausnahme nicht.

Beim Auftreten einer Ausnahme in einem Autorisierungsfilter sollten Sie eine Abfrage erwägen.

Weitere Informationen finden Sie unter Autorisierung.

Ressourcenfilter

Ressourcenfilter:

Ressourcenfilter sind hilfreich, um den größten Teil der Pipeline kurzzuschließen. Ein Cachingfilter kann beispielsweise bei einem Cachetreffer den Rest der Pipeline überspringen.

Beispiele für Ressourcenfilter:

Aktionsfilter

Aktionsfilter gelten nicht für Razor Pages. Razor Pages unterstützt IPageFilter und IAsyncPageFilter. Weitere Informationen finden Sie unter Filtermethoden für Razor Pages.

Für Aktionsfilter gilt Folgendes:

Der folgende Code zeigt einen Beispielaktionsfilter:

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.
    }
}

ActionExecutingContext bietet folgende Eigenschaften:

  • ActionArguments ermöglicht das Lesen der Eingaben für eine Aktionsmethode.
  • Controller ermöglicht das Ändern der Controllerinstanz.
  • Result: Die Festlegung von Result schließt die Ausführung der Aktionsmethode und der nachfolgenden Aktionsfilter kurz.

Durch Auslösen einer Ausnahme in einer Aktionsmethode geschieht Folgendes:

  • Die Ausführung nachfolgender Filter wird verhindert.
  • Im Gegensatz zum Festlegen von Result wird die ausgelöste Ausnahme wird nicht als erfolgreiches Ergebnis, sondern als Fehler behandelt.

ActionExecutedContext bietet Controller und Result sowie die folgenden Eigenschaften:

  • Canceled ist TRUE, wenn die Aktionsausführung durch einen anderen Filter kurzgeschlossen wurde.
  • Exception ist ungleich NULL, wenn die Aktion oder ein zuvor ausgeführter Aktionsfilter eine Ausnahme ausgelöst hat. Durch Festlegen dieser Eigenschaft auf NULL geschieht Folgendes:
    • Die Ausnahme wird effektiv behandelt.
    • Result wird so ausgeführt, als wäre das Ergebnis von der Aktionsmethode zurückgegeben worden.

Bei einem IAsyncActionFilter hat der Aufruf von ActionExecutionDelegate folgende Auswirkungen:

  • Alle nachfolgenden Aktionsfilter und die Aktionsmethode werden ausgeführt.
  • Gibt ActionExecutedContext zurück.

Weisen Sie einer Ergebnisinstanz Microsoft.AspNetCore.Mvc.Filters.ActionExecutingContext.Result zu, und rufen Sie nicht next (den ActionExecutionDelegate) auf, um die Pipeline kurzzuschließen.

Das Framework stellt ein abstraktes ActionFilterAttribute bereit, das als Unterklasse verwendet werden kann.

Der Aktionsfilter OnActionExecuting kann für Folgendes verwendet werden:

  • Überprüfen des Modellzustands.
  • Zurückgeben eines Fehlers, wenn der Zustand ungültig ist.
public class ValidateModelAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext context)
    {
        if (!context.ModelState.IsValid)
        {
            context.Result = new BadRequestObjectResult(context.ModelState);
        }
    }
}

Hinweis

Controller, die mit dem [ApiController]-Attribut kommentiert werden, überprüfen automatisch den Modellzustand und geben eine 400-Antwort zurück. Weitere Informationen finden Sie unter Automatische HTTP 400-Antworten.

Die Methode OnActionExecuted wird nach der Aktionsmethode ausgeführt. Es gilt Folgendes:

  • Die Methode kann die Ergebnisse der Aktion durch die Eigenschaft Result sehen und ändern.
  • Canceled wird auf TRUE festgelegt, wenn die Ausführung der Aktion durch einen anderen Filter kurzgeschlossen wurde.
  • Exception wird auf einen Wert ungleich NULL festgelegt, wenn die Aktion oder ein nachfolgender Aktionsfilter eine Ausnahme ausgelöst hat. Wenn für Exception NULL festgelegt wird,
    • werden Ausnahmen effektiv behandelt,
    • und ActionExecutedContext.Result wird so ausgeführt, als würde die Eigenschaft normal von der Aktionsmethode zurückgegeben werden.

Ausnahmefilter

Für Ausnahmefilter gilt Folgendes:

Der folgende Beispielausnahmefilter zeigt Details zu Ausnahmen an, die auftreten, wenn die App sich in der Entwicklung befindet:

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()
        };
    }
}

Mit dem folgenden Code wird der Ausnahmefilter getestet:

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

Für Ausnahmefilter gilt Folgendes:

  • Sie verfügen nicht über vorangehende oder darauffolgende Ereignisse.
  • Sie implementieren OnException oder OnExceptionAsync.
  • Sie behandeln Ausnahmefehler, die bei der Erstellung von Razor Pages oder Controllern, bei der Modellbindung, bei Aktionsfiltern oder bei Aktionsmethoden auftreten.
  • Sie erfassen keine Ausnahmen, die in Ressourcenfiltern, Ergebnisfiltern oder bei der Ausführung von MVC-Ergebnissen auftreten.

Um eine Ausnahme zu behandeln, legen Sie die Eigenschaft ExceptionHandled auf true fest, oder weisen Sie die Eigenschaft Result zu. Dadurch wird die Weitergabe der Ausnahme beendet. Ein Ausnahmefilter kann eine Ausnahme nicht in ein „erfolgreiches Ergebnis“ umwandeln. Nur ein Aktionsfilter kann das.

Für Ausnahmefilter gilt Folgendes:

  • Sie eignen sich für das Abfangen von Ausnahmen, die in Aktionen auftreten.
  • Sie sind im Vergleich zu Middleware für die Fehlerbehandlung weniger flexibel.

Sie sollten bei der Ausnahmebehandlung vorzugsweise Middleware verwenden. Verwenden Sie Ausnahmefilter nur, wenn sich die Fehlerbehandlung unterscheidet, je nachdem, welche Aktionsmethode aufgerufen wird. Zum Beispiel verfügt eine App möglicherweise über Aktionsmethoden für beide API-Endpunkte und für Ansichten bzw. HTML. Die API-Endpunkte können Fehlerinformationen als JSON zurückgeben, während ansichtsbasierte Aktionen eine Fehlerseite als HTML zurückgeben können.

Ergebnisfilter

Für Ergebnisfilter gilt:

IResultFilter und IAsyncResultFilter

Der folgende Code zeigt einen Beispielergebnisfilter:

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.
    }
}

Die Art des Ergebnisses, das ausgeführt wird, hängt von der jeweiligen Aktion ab. Eine Aktion, die eine Ansicht zurückgibt, beinhaltet die gesamte Razor-Verarbeitung als Teil der Ausführung von ViewResult. Eine API-Methode führt möglicherweise eine Art Serialisierung als Teil der Ausführung des Ergebnisses durch. Erfahren Sie mehr zu Aktionsergebnissen.

Ergebnisfilter werden nur ausgeführt, wenn eine Aktion oder ein Aktionsfilter ein Aktionsergebnis liefert. Ergebnisfilter werden in folgenden Fällen nicht ausgeführt:

  • Ein Autorisierungs- oder Ressourcenfilter schließt die Pipeline kurz.
  • Ein Ausnahmefilter behandelt eine Ausnahme durch Erzeugen eines Aktionsergebnisses.

Die Methode Microsoft.AspNetCore.Mvc.Filters.IResultFilter.OnResultExecuting kann die Ausführung des Aktionsergebnisses und der nachfolgenden Ergebnisfilter durch Festlegen von Microsoft.AspNetCore.Mvc.Filters.ResultExecutingContext.Cancel auf true kurzschließen. Schreiben Sie beim Kurzschließen in das Antwortobjekt, um zu verhindern, dass eine leere Antwort generiert wird. Das Auslösen einer Ausnahme in IResultFilter.OnResultExecuting bewirkt Folgendes:

  • Die Ausführung des Aktionsergebnisses und der nachfolgenden Filter wird verhindert.
  • Die ausgelöste Ausnahme wird nicht als erfolgreiches Ergebnis, sondern als Fehler behandelt.

Wenn die Microsoft.AspNetCore.Mvc.Filters.IResultFilter.OnResultExecuted-Methode ausgeführt wird, wurde die Antwort wahrscheinlich bereits an den Client gesendet. Wenn die Antwort bereits an den Client gesendet wurde, kann sie nicht geändert werden.

ResultExecutedContext.Canceled wird auf true festgelegt, wenn die Ausführung des Aktionsergebnisses durch einen anderen Filter kurzgeschlossen wurde.

ResultExecutedContext.Exception wird auf einen Wert ungleich NULL festgelegt, wenn das Aktionsergebnis oder ein nachfolgender Ergebnisfilter eine Ausnahme ausgelöst hat. Das Festlegen von Exception auf NULL behandelt eine Ausnahme und verhindert, dass die Ausnahme zu einem späteren Zeitpunkt in der Pipeline ein weiteres Mal ausgelöst wird. Beim Behandeln einer Ausnahme in einem Ergebnisfilter gibt es keine zuverlässige Möglichkeit, Daten in eine Antwort zu schreiben. Wenn ein Aktionsergebnis eine Ausnahme auslöst und die Header bereits an den Client übergeben wurden, gibt es keinen zuverlässigen Mechanismus, einen Fehlercode zu senden.

Bei einem IAsyncResultFilter führt ein Aufruf von await next im ResultExecutionDelegate alle nachfolgenden Ergebnisfilter und das Aktionsergebnis aus. Legen Sie ResultExecutingContext.Cancel auf true fest, und rufen Sie nicht ResultExecutionDelegate auf, um die Pipeline kurzzuschließen.

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

Das Framework stellt ein abstraktes ResultFilterAttribute bereit, das als Unterklasse verwendet werden kann. Die weiter oben gezeigte Klasse ResponseHeaderAttribute ist ein Beispiel für ein Ergebnisfilterattribut.

IAlwaysRunResultFilter und IAsyncAlwaysRunResultFilter

Die Schnittstellen IAlwaysRunResultFilter und IAsyncAlwaysRunResultFilter deklarieren eine IResultFilter-Implementierung, die für alle Aktionsergebnisse ausgeführt wird. Dies schließt Aktionsergebnisse ein, die von folgenden Filtern generiert werden:

  • Autorisierungs- und Ressourcenfilter, die einen Kurzschluss bewirken
  • Ausnahmefilter

Beispielsweise wird der folgende Filter immer ausgeführt und legt ein Aktionsergebnis (ObjectResult) mit dem Statuscode 422 – Einheit kann nicht bearbeitet werden fest, wenn bei der Inhaltsaushandlung ein Fehler auftritt:

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 implementiert IFilterMetadata. Deshalb kann eine IFilterFactory-Instanz an einer beliebigen Stelle in der Filterpipeline als IFilterMetadata-Instanz verwendet werden. Wenn die Runtime den Aufruf des Filters vorbereitet, versucht sie, ihn in eine IFilterFactory umzuwandeln. Wenn diese Umwandlung gelingt, wird die Methode CreateInstance aufgerufen, um die Instanz IFilterMetadata für den Aufruf zu erstellen. Da die exakte Filterpipeline beim Start der Anwendung nicht explizit festgelegt werden muss, wird dadurch ein sehr flexibles Design ermöglicht.

IFilterFactory.IsReusable:

  • Ist ein Hinweis der Factory darauf, dass die von der Factory erstellte Filterinstanz möglicherweise außerhalb des Anforderungsbereichs, in dem sie erstellt wurde, wiederverwendet wird
  • Sollte nicht mit einem Filter verwendet werden, der von Diensten mit einer anderen Lebensdauer als der eines Singletons abhängig ist

Die ASP.NET Core-Runtime bietet keine Garantie für Folgendes:

  • Eine einzelne Instanz des Filters wird erstellt.
  • Der Filter wird nicht zu einem späteren Zeitpunkt vom DI-Container erneut angefordert.

Warnung

Konfigurieren Sie IFilterFactory.IsReusable nur so, dass true zurückgegeben wird, wenn die Quelle der Filter eindeutig ist, die Filter zustandslos sind und die Filter sicher in mehreren HTTP-Anforderungen verwendet werden können. Geben Sie beispielsweise keine Filter aus DI zurück, die als bereichsbezogen oder vorübergehend registriert sind, wenn IFilterFactory.IsReusabletrue zurückgibt.

Als weiteres Verfahren zum Erstellen von Filtern kann IFilterFactory mithilfe von benutzerdefinierten Attributimplementierungen implementiert werden:

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) { }
    }

Der Filter wird im folgenden Code angewendet:

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

In einem Attribut implementierte IFilterFactory

Filter, die IFilterFactory implementieren, eignen sich in folgenden Fällen:

  • Sie erfordern kein Übergeben von Parametern.
  • Sie verfügen über Konstruktorabhängigkeiten, die durch DI erfüllt werden müssen.

TypeFilterAttribute implementiert IFilterFactory. IFilterFactory macht die CreateInstance-Methode zum Erstellen einer IFilterMetadata-Instanz verfügbar. CreateInstance lädt den angegebenen Typ aus dem Dienstecontainer (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)}");
        }
    }
}

Der folgende Code zeigt drei Ansätze zum Anwenden des Filters:

[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)}");

Im obigen Code wird der erste Ansatz zum Anwenden des Filters bevorzugt.

Verwenden von Middleware in der Filterpipeline

Ressourcenfilter funktionieren wie Middleware, sie umschließen alle Ausführungen, die später in der Pipeline enthalten sind. Filter unterscheiden sich jedoch insofern von Middleware, dass sie Teil der Runtime sind, was bedeutet, dass sie Zugriff auf Kontext und Konstrukte haben.

Um Middleware als Filter zu verwenden, erstellen Sie einen Typ mit einer Configure-Methode, die die Middleware festlegt, die in die Filterpipeline eingefügt werden soll. Im folgenden Beispiel wird Middleware verwendet, um einen Antwortheader festzulegen:

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

            await next();
        });
    }
}

Verwenden Sie das MiddlewareFilterAttribute zum Ausführen der Middleware:

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

Middlewarefilter werden zum selben Zeitpunkt in der Filterpipeline wie Ressourcenfilter, vor der Modellbindung und nach dem Rest der Pipeline ausgeführt.

Threadsicherheit

Wenn eine Instanz eines Filters in Add statt in Type übergeben wird, ist der Filter ein Singleton und ist nicht threadsicher.

Zusätzliche Ressourcen

Von Kirk Larkin, Rick Anderson, Tom Dykstra und Steve Smith

Filter ermöglichen es in ASP.NET Core, Code vor oder nach bestimmten Phasen der Anforderungsverarbeitungspipeline auszuführen.

Integrierte Filter sind für folgende Aufgaben zuständig:

  • Autorisierung (der Zugriff auf Ressourcen, für die ein Benutzer nicht autorisiert ist, wird verhindert)
  • Zwischenspeicherung von Antworten (die Anforderungspipeline wird unterbrochen, damit eine zwischengespeicherte Antwort zurückgegeben wird)

Durch die Erstellung benutzerdefinierter Filter kann mit aktionsübergreifenden Problemen umgegangen werden. Zu solchen übergreifenden Problemen gehören beispielsweise Fehlerbehandlung, Caching, Konfiguration, Autorisierung und Protokollierung. Mit Filtern lässt sich die Duplizierung von Code vermeiden. Sie können zum Beispiel die Fehlerbehandlung in einem Ausnahmefilter konsolidieren.

Dieses Dokument gilt für Razor Pages, API-Controller und Controller mit Ansichten. Filter können nicht direkt mit Razor-Komponenten verwendet werden. Ein Filter kann nur indirekt Einfluss auf eine Komponente haben, wenn Folgendes gilt:

  • Die Komponente ist in eine Seite oder Ansicht eingebettet.
  • Die Seite oder der Controller und die Ansicht verwenden den Filter.

Zeigen Sie ein Beispiel an, oder laden Sie es herunter (Vorgehensweise zum Herunterladen).

Die Funktionsweise von Filtern

Filter werden in der ASP.NET Core-Aktionsaufrufpipeline ausgeführt, die manchmal auch als Filterpipeline bezeichnet wird. Die Filterpipeline wird ausgeführt, nachdem ASP.NET Core die auszuführende Aktion ausgewählt hat.

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.

Filtertypen

Jeder Filtertyp wird in einer anderen Phase der Filterpipeline ausgeführt:

  • Autorisierungsfilter werden zuerst ausgeführt und dienen dazu, festzustellen, ob der Benutzer für die Anforderung berechtigt ist. Autorisierungsfilter schließen die Pipeline kurz, wenn die Anforderung nicht autorisiert ist.

  • Ressourcenfilter:

    • Werden nach der Autorisierung ausgeführt.
    • OnResourceExecuting führt Code vor dem Rest der Filterpipeline aus. Beispielsweise führt OnResourceExecuting Code vor der Modellbindung aus.
    • OnResourceExecuted führt Code aus, nachdem der Rest der Pipeline abgeschlossen wurde.
  • Für Aktionsfilter gilt Folgendes:

    • Sie führen Code unmittelbar vor und nach dem Aufruf einer Aktionsmethode aus.
    • Sie können die an eine Aktion übergebenen Argumente ändern.
    • Sie können das von der Aktion zurückgegebene Ergebnis ändern.
    • Sie werden in Razor Pages nicht unterstützt.
  • Ausnahmefilter wenden globale Richtlinien auf unbehandelte Ausnahmen an, die auftreten, bevor etwas in den Antworttext geschrieben wurde.

  • Ergebnisfilter führen Code unmittelbar vor und nach der Ausführung von Aktionsergebnissen aus. Sie werden nur ausgeführt, wenn die Aktionsmethode erfolgreich ausgeführt wurde. Ergebnisfilter sind hilfreich für Logik, die die Ausführung von Ansichten oder Formatierern umfasst.

Das folgende Diagramm veranschaulicht, wie die Filtertypen mit der Filterpipeline interagieren.

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.

Implementierung

Filter unterstützen sowohl synchrone als auch asynchrone Implementierungen über verschiedene Schnittstellendefinitionen.

Synchrone Filter führen Code vor und nach der jeweiligen Pipelinephase aus. OnActionExecuting beispielsweise wird aufgerufen, bevor die Aktionsmethode aufgerufen wird. OnActionExecuted wird nach der Rückgabe der Aktionsmethode aufgerufen.

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);
    }
}

Im vorherigen Code ist MyDebug eine Hilfsprogrammfunktion im Beispieldownload.

Asynchrone Filter definieren eine On-Stage-ExecutionAsync-Methode. Beispiel: 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.
    }
}

Im oben stehenden Code weist der SampleAsyncActionFilter einen ActionExecutionDelegate (next) auf, der die Aktionsmethode ausführt.

Mehrere Filterphasen

Schnittstellen für mehrere Filterphasen können in einer einzigen Klasse implementiert werden. Beispielsweise implementiert die ActionFilterAttribute-Klasse:

Implementieren Sie entweder die synchrone oder asynchrone Version einer Filterschnittstelle, aber nicht beide. Die Runtime prüft zuerst, ob der Filter die asynchrone Schnittstelle implementiert, und wenn dies der Fall ist, ruft die Runtime diese Schnittstelle auf. Wenn dies nicht der Fall ist, ruft es die Methode(n) der synchronen Schnittstelle auf. Wenn die asynchrone und die synchrone Schnittstelle in einer einzigen Klasse implementiert sind, wird nur die asynchrone Methode aufgerufen. Bei Verwendung von abstrakten Klassen wie ActionFilterAttribute überschreiben Sie nur die synchronen Methoden oder die asynchrone Methode für jeden Filtertyp.

Integrierte Filterattribute

ASP.NET Core enthält integrierte attributbasierte Filter, die als Unterklasse erstellt und angepasst werden können. Zum Beispiel fügt der folgende Ergebnisfilter der Antwort einen Header hinzu:

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);
    }
}

Wie im obigen Beispiel gezeigt, können Filter mithilfe von Attributen Argumente akzeptieren. Wenden Sie das AddHeaderAttribute auf einen Controller oder eine Aktionsmethode an, und geben Sie den Namen und den Wert des HTTP-Headers an:

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

Verwenden Sie ein Tool wie die Browser-Entwicklertools, um die Header zu untersuchen. Unter Antwortheader wird author: Rick Anderson angezeigt.

Mit dem folgenden Code wird ein ActionFilterAttribute-Element implementiert, das diese Aktionen ausführt:

  • Liest den Titel und den Namen aus dem Konfigurationssystem. Im Gegensatz zum vorherigen Beispiel erfordert der folgende Code nicht, dass dem Code Filterparameter hinzugefügt werden.
  • Fügt dem Antwortheader den Titel und den Namen hinzu.
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);
    }
}

Die Konfigurationsoptionen werden vom Konfigurationssystem mithilfe der Optionsmuster bereitgestellt. Beispiel aus der appsettings.json-Datei:

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

In StartUp.ConfigureServices:

  • Die PositionOptions-Klasse wird dem Dienstcontainer mit dem Konfigurationsbereich "Position" hinzugefügt.
  • MyActionFilterAttribute wird dem Dienstcontainer hinzugefügt.
public void ConfigureServices(IServiceCollection services)
{
    services.Configure<PositionOptions>(
             Configuration.GetSection("Position"));
    services.AddScoped<MyActionFilterAttribute>();

    services.AddControllersWithViews();
}

Der folgende Code zeigt die PositionOptions-Klasse:

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

Der folgende Code wendet das MyActionFilterAttribute auf die Methode Index2 an:

[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.");
    }

Unter Antwortheader werden author: Rick Anderson und Editor: Joe Smith angezeigt, wenn der Sample/Index2-Endpunkt aufgerufen wird.

Der folgende Code wendet das MyActionFilterAttribute und das AddHeaderAttribute auf die Razor-Seite an:

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

Filter können nicht auf Razor Page-Handlermethoden angewendet werden. Sie können entweder auf das Razor Page-Modell oder global angewendet werden.

Einige der Filterschnittstellen besitzen entsprechende Attribute, die als Basisklassen für benutzerdefinierte Implementierungen verwendet werden können.

Filterattribute:

Filterbereiche und Reihenfolge der Ausführung

Der Pipeline kann in einem von drei Bereichen ein Filter hinzugefügt werden:

  • Verwenden eines Attributs für eine Controller-Aktion. Es können keine Filterattribute auf Handlermethoden von Razor Pages angewendet werden.
  • Verwenden eines Attributs für einen Controller oder eine Razor-Seite
  • Global für alle Controller und Aktionen, Razor Pages wie im folgenden Code gezeigt:
public void ConfigureServices(IServiceCollection services)
{
    services.AddControllersWithViews(options =>
   {
        options.Filters.Add(typeof(MySampleActionFilter));
    });
}

Standardreihenfolge der Ausführung

Wenn es mehrere Filter für eine bestimmte Stufe der Pipeline gibt, bestimmt der Bereich die Standardreihenfolge der Filterausführung. Globale Filter umfassen Klassenfilter, welche hingegen Methodenfilter umfassen.

Als Ergebnis der Filterschachtelung wird der nach einer Pipelinephase auszuführende Code in umgekehrter Reihenfolge zum Code vor einer Pipelinephase ausgeführt. Filterreihenfolge:

  • Der Code von globalen Filtern, der vor einer Pipelinephase ausgeführt werden soll
    • Der Code von Controllerfiltern und Razor Page-Filtern, der vor einer Pipelinephase ausgeführt werden soll
      • Der Code von Aktionsmethodenfiltern, der vor einer Pipelinephase ausgeführt werden soll
      • Der Code von Aktionsmethodenfiltern, der nach einer Pipelinephase ausgeführt werden soll
    • Der Code von Controllerfiltern und Razor Page-Filtern, der nach einer Pipelinephase ausgeführt werden soll
  • Der Code von globalen Filtern, der nach einer Pipelinephase ausgeführt werden soll

Das folgende Beispiel veranschaulicht die Reihenfolge, in der Filtermethoden für synchrone Aktionsfilter aufgerufen werden.

Sequence Filterbereich Filtermethode
1 Global OnActionExecuting
2 Controller oder Razor-Seite OnActionExecuting
3 Methode OnActionExecuting
4 Methode OnActionExecuted
5 Controller oder Razor-Seite OnActionExecuted
6 Global OnActionExecuted

Filter auf Controllerebene

Jeder Controller, der von der Basisklasse Controller erbt, enthält die Methoden Controller.OnActionExecuting, Controller.OnActionExecutionAsync und Controller.OnActionExecutedOnActionExecuted. Diese Methoden führen Folgendes aus:

  • Die Filter, die für eine bestimmte Aktion ausgeführt werden, werden umschlossen.
  • OnActionExecuting wird vor allen Filtern der Aktion aufgerufen.
  • OnActionExecuted wird nach allen Filtern der Aktion aufgerufen.
  • OnActionExecutionAsync wird vor allen Filtern der Aktion aufgerufen. Code im Filter nach next wird nach der Aktionsmethode ausgeführt.

Im Downloadbeispiel wird MySampleActionFilter global beim Start angewendet.

Die TestController:

  • Wendet das SampleActionFilterAttribute ([SampleActionFilter]) auf die Aktion FilterTest2 an.
  • Überschreibt OnActionExecuting und 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 wird von dem NuGet-Paket Rick.Docs.Samples.RouteInfo bereitgestellt und zeigt Routeninformationen an.

Durch Navigieren zu https://localhost:5001/Test/FilterTest2 wird der folgende Code ausgeführt:

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

Filter auf Controllerebene legen die Eigenschaft Order auf int.MinValue fest. Für Filter auf Controllerebene kann die Ausführung nach Filtern, die auf Methoden angewendet werden nicht festgelegt werden. Die Reihenfolge wird im nächsten Abschnitt erläutert.

Weitere Informationen zu Razor Pages finden Sie unter Implementieren von Razor Page-Filtern durch das Überschreiben von Filtermethoden.

Überschreiben der Standardreihenfolge

Die Standardreihenfolge der Ausführung kann durch Implementieren von IOrderedFilter überschrieben werden. IOrderedFilter macht die Eigenschaft Order verfügbar, die bei der Bestimmung der Ausführungsreihenfolge Vorrang vor dem Bereich hat. Für einen Filter mit geringerem Order-Wert gilt:

  • Führt den Code, der vor einer Pipelinephase ausgeführt werden soll, vor dem Code eines Filters mit einem höheren Wert für Order aus.
  • Führt den Code, der nach einer Pipelinephase ausgeführt werden soll, nach dem Code eines Filters mit einem höheren Wert für Order aus.

Die Eigenschaft Order wird mit einem Konstruktorparameter festgelegt:

[SampleActionFilter(Order = int.MinValue)]

Sehen Sie sich die zwei Aktionsfilter im folgenden Controller an:

[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);
    }
}

In StartUp.ConfigureServices wird ein globaler Filter hinzugefügt:

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

Die 3 Filter werden in der folgenden Reihenfolge ausgeführt:

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

Die Eigenschaft Order hat bei der Bestimmung der Reihenfolge, in der Filter ausgeführt werden, Vorrang vor dem Bereich. Filter werden erst nach der Reihenfolge sortiert, und dann werden Gleichstände über den Bereich aufgelöst. Alle integrierten Filter implementieren IOrderedFilter und legen den Standartwert von Order auf 0 fest. Wie zuvor erwähnt, legen Filter auf Controllerebene die Order-Eigenschaft auf int.MinValue fest. Bei integrierten Filtern bestimmt der Umfang die Reihenfolge, es sei denn, Order ist auf einen Wert ungleich null festgelegt.

Im vorstehenden Code hat MySampleActionFilter globalen Umfang und wird daher vor MyAction2FilterAttribute ausgeführt, der Controllerumfang aufweist. Um MyAction2FilterAttribute zuerst ausführen zu lassen, legen Sie die Reihenfolge auf int.MinValue fest:

[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);
    }
}

Um zuerst den globalen Filter MySampleActionFilter ausführen zu lassen, legen Sie Order auf int.MinValue fest:

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

Abbrechen und Kurzschließen

Die Filterpipeline kann kurzgeschlossen werden, indem die Eigenschaft Result auf den Parameter ResourceExecutingContext festgelegt wird, der in der Filtermethode angegeben ist. Zum Beispiel verhindert der folgende Ressourcenfilter, dass der Rest der Pipeline ausgeführt wird:

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)
    {
    }
}

Im folgenden Code verwenden sowohl der Filter ShortCircuitingResourceFilter und der Filter AddHeader die Aktionsmethode SomeResource als Ziel. Die ShortCircuitingResourceFilter:

  • Der Filter wird zuerst ausgeführt, da es sich um einen Ressourcenfilter handelt und AddHeader ein Aktionsfilter ist.
  • Der Filter unterbricht alle verbleibenden Pipelineschritte.

Der AddHeader-Filter wird daher nie für die SomeResource-Aktion ausgeführt. Dasselbe Verhalten würde auch dann auftreten, wenn beide Filter auf Aktionsmethodenebene angewendet würden, wenn ShortCircuitingResourceFilter zuerst ausgeführt wird. Der ShortCircuitingResourceFilter wird aufgrund seines Filtertyps oder durch die explizite Verwendung der Order-Eigenschaft zuerst ausgeführt.

[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.");
    }
}

Abhängigkeitsinjektion

Filter können nach Typ oder Instanz hinzugefügt werden. Wenn eine Instanz hinzugefügt wird, wird diese Instanz für jede Anforderung verwendet. Wenn ein Typ hinzugefügt wird, ist der Filter typaktiviert. Ein typaktivierter Filter bedeutet Folgendes:

  • Für jede Anforderung wird eine Instanz erstellt.
  • Alle Konstruktorabhängigkeiten werden durch Dependency Injection (DI) aufgefüllt.

Filter, die als Attribute implementiert und Controllerklassen oder Aktionsmethoden direkt hinzugefügt werden, können keine Konstruktorabhängigkeiten von Dependency Injection (DI) erhalten. Konstruktorabhängigkeiten können aus folgenden Gründen von DI nicht bereitgestellt werden:

  • Für Attribute müssen Konstruktorparameter bereitgestellt werden, wenn sie angewendet werden.
  • Dies ist eine Einschränkung der Funktionsweise von Attributen.

Die folgenden Filter unterstützen von DI bereitgestellte Konstruktorabhängigkeiten:

Die oben genannten Filter können auf einen Controller oder eine Aktionsmethode angewendet werden:

DI bietet Protokollierungen. Vermeiden Sie es jedoch, Filter nur zum Zweck der Protokollierung zu erstellen und zu verwenden. Die integrierte Frameworkprotokollierung bietet in der Regel alle Elemente, die für die Protokollierung erforderlich sind. Wenn Protokollierung zu Filtern hinzugefügt wird, gilt Folgendes:

  • Die Protokollierung sollte mit Schwerpunkt auf geschäftlichen Aspekten oder verhaltensspezifisch für den Filter erfolgen.
  • Aktionen oder andere Frameworkereignisse sollten nicht protokolliert werden. Die integrierten Filter protokollieren Aktionen und Frameworkereignisse.

ServiceFilterAttribute

Die Typen der Dienstfilterimplementierung werden in ConfigureServices registriert. Ein ServiceFilterAttribute ruft eine Instanz des Filter von DI ab.

Der folgende Code zeigt den 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");
    }
}

Im folgenden Code wird AddHeaderResultServiceFilter zum DI-Container hinzugefügt:

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
    });
}

Im folgenden Code ruft das ServiceFilter-Attribut eine Instanz des AddHeaderResultServiceFilter-Filters aus DI ab:

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

Wenn Sie ServiceFilterAttribute verwenden, bewirkt das Festlegen von ServiceFilterAttribute.IsReusable Folgendes:

  • Gibt einen Hinweis darauf, dass die Filterinstanz möglicherweise außerhalb des Anforderungsbereichs, in dem sie erstellt wurde, wiederverwendet wird. Die ASP.NET Core-Runtime bietet keine Garantie für Folgendes:

    • Eine einzelne Instanz des Filters wird erstellt.
    • Der Filter wird nicht zu einem späteren Zeitpunkt vom DI-Container erneut angefordert.
  • Sollte nicht mit einem Filter verwendet werden, der von Diensten mit einer anderen Lebensdauer als der eines Singletons abhängig ist

ServiceFilterAttribute implementiert IFilterFactory. IFilterFactory macht die CreateInstance-Methode zum Erstellen einer IFilterMetadata-Instanz verfügbar. CreateInstance lädt den angegebenen Typ aus DI.

TypeFilterAttribute

TypeFilterAttribute ähnelt ServiceFilterAttribute. Der zugehörige Typ wird allerdings nicht direkt vom DI-Container aufgelöst. Stattdessen wird der Typ mithilfe von Microsoft.Extensions.DependencyInjection.ObjectFactory instanziiert.

Da TypeFilterAttribute-Typen nicht direkt vom DI-Container aufgelöst werden, gilt Folgendes:

  • Typen, auf die mithilfe von TypeFilterAttribute verwiesen wird, müssen nicht beim DI-Container registriert werden. Ihre Abhängigkeiten werden vom DI-Container erfüllt.
  • TypeFilterAttribute kann optional Konstruktorargumente für den Typ akzeptieren.

Wenn Sie TypeFilterAttribute verwenden, bewirkt das Festlegen von TypeFilterAttribute.IsReusable Folgendes:

  • Gibt einen Hinweis darauf, dass die Filterinstanz möglicherweise außerhalb des Anforderungsbereichs wiederverwendet wird, in dem sie erstellt wurde. Die ASP.NET Core-Runtime bietet keine Garantie dafür, dass eine einzelne Instanz des Filters erstellt wird.

  • Sollte nicht mit einem Filter verwendet werden, der von Diensten mit einer anderen Lebensdauer als der eines Singletons abhängig ist

Das folgende Beispiel zeigt, wie Sie Argumente durch Verwendung von TypeFilterAttribute an einen Typ übergeben:

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

Autorisierungsfilter

Für Autorisierungsfilter gilt Folgendes:

  • Sie werden als erste Filter in der Filterpipeline ausgeführt.
  • Sie steuern den Zugriff auf Aktionsmethoden.
  • Sie verfügen nur über eine Methode, die vor der Aktion ausgeführt wird, nicht jedoch über eine Methode, die nach der Aktion ausgeführt wird.

Benutzerdefinierte Autorisierungsfilter erfordern ein benutzerdefiniertes Autorisierungsframework. Statt einen benutzerdefinierten Filter zu schreiben, sollten Sie die Autorisierungsrichtlinien konfigurieren oder eine benutzerdefinierte Autorisierungsrichtlinie schreiben. Für den integrierten Autorisierungsfilter gilt:

  • Er ruft das Autorisierungssystem auf.
  • Er autorisiert keine Anforderungen.

Lösen Sie keine Ausnahmen in Autorisierungsfiltern aus:

  • Die Ausnahme wird nicht behandelt.
  • Ausnahmefilter behandeln die Ausnahme nicht.

Beim Auftreten einer Ausnahme in einem Autorisierungsfilter sollten Sie eine Abfrage erwägen.

Weitere Informationen finden Sie unter Autorisierung.

Ressourcenfilter

Ressourcenfilter:

Ressourcenfilter sind hilfreich, um den größten Teil der Pipeline kurzzuschließen. Ein Cachingfilter kann beispielsweise bei einem Cachetreffer den Rest der Pipeline überspringen.

Beispiele für Ressourcenfilter:

Aktionsfilter

Aktionsfilter gelten nicht für Razor Pages. Razor Pages unterstützt IPageFilter und IAsyncPageFilter. Weitere Informationen finden Sie unter Filtermethoden für Razor Pages.

Für Aktionsfilter gilt Folgendes:

Der folgende Code zeigt einen Beispielaktionsfilter:

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);
    }
}

ActionExecutingContext bietet folgende Eigenschaften:

  • ActionArguments ermöglicht das Lesen der Eingaben für eine Aktionsmethode.
  • Controller ermöglicht das Ändern der Controllerinstanz.
  • Result: Die Festlegung von Result schließt die Ausführung der Aktionsmethode und der nachfolgenden Aktionsfilter kurz.

Durch Auslösen einer Ausnahme in einer Aktionsmethode geschieht Folgendes:

  • Die Ausführung nachfolgender Filter wird verhindert.
  • Im Gegensatz zum Festlegen von Result wird die ausgelöste Ausnahme wird nicht als erfolgreiches Ergebnis, sondern als Fehler behandelt.

ActionExecutedContext bietet Controller und Result sowie die folgenden Eigenschaften:

  • Canceled ist TRUE, wenn die Aktionsausführung durch einen anderen Filter kurzgeschlossen wurde.

  • Exception ist ungleich NULL, wenn die Aktion oder ein zuvor ausgeführter Aktionsfilter eine Ausnahme ausgelöst hat. Durch Festlegen dieser Eigenschaft auf NULL geschieht Folgendes:

    • Die Ausnahme wird effektiv behandelt.
    • Result wird so ausgeführt, als wäre das Ergebnis von der Aktionsmethode zurückgegeben worden.

Bei einem IAsyncActionFilter hat der Aufruf von ActionExecutionDelegate folgende Auswirkungen:

  • Alle nachfolgenden Aktionsfilter und die Aktionsmethode werden ausgeführt.
  • Gibt ActionExecutedContext zurück.

Weisen Sie einer Ergebnisinstanz Microsoft.AspNetCore.Mvc.Filters.ActionExecutingContext.Result zu, und rufen Sie nicht next (den ActionExecutionDelegate) auf, um die Pipeline kurzzuschließen.

Das Framework stellt ein abstraktes ActionFilterAttribute bereit, das als Unterklasse verwendet werden kann.

Der Aktionsfilter OnActionExecuting kann für Folgendes verwendet werden:

  • Überprüfen des Modellzustands.
  • Zurückgeben eines Fehlers, wenn der Zustand ungültig ist.
public class ValidateModelAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext 
                                           context)
    {
        if (!context.ModelState.IsValid)
        {
            context.Result = new BadRequestObjectResult(
                                                context.ModelState);
        }
    }

Hinweis

Controller, die mit dem [ApiController]-Attribut kommentiert werden, überprüfen automatisch den Modellzustand und geben eine 400-Antwort zurück. Weitere Informationen finden Sie unter Automatische HTTP 400-Antworten. Die Methode OnActionExecuted wird nach der Aktionsmethode ausgeführt. Es gilt Folgendes:

  • Die Methode kann die Ergebnisse der Aktion durch die Eigenschaft Result sehen und ändern.

  • Canceled wird auf TRUE festgelegt, wenn die Ausführung der Aktion durch einen anderen Filter kurzgeschlossen wurde.

  • Exception wird auf einen Wert ungleich NULL festgelegt, wenn die Aktion oder ein nachfolgender Aktionsfilter eine Ausnahme ausgelöst hat. Wenn für Exception NULL festgelegt wird,

    • werden Ausnahmen effektiv behandelt,
    • und ActionExecutedContext.Result wird so ausgeführt, als würde die Eigenschaft normal von der Aktionsmethode zurückgegeben werden.
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);
    }
}

Ausnahmefilter

Für Ausnahmefilter gilt Folgendes:

Der folgende Beispielausnahmefilter verwendet eine benutzerdefinierte Fehleransicht, um Details zu Ausnahmen anzuzeigen, die auftreten, wenn die App sich in der Entwicklung befindet:

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;
    }
}

Mit dem folgenden Code wird der Ausnahmefilter getestet:

[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.");
    }
}

Für Ausnahmefilter gilt Folgendes:

  • Sie verfügen nicht über vorangehende oder darauffolgende Ereignisse.
  • Sie implementieren OnException oder OnExceptionAsync.
  • Sie behandeln Ausnahmefehler, die bei der Erstellung von Razor Pages oder Controllern, bei der Modellbindung, bei Aktionsfiltern oder bei Aktionsmethoden auftreten.
  • Sie erfassen keine Ausnahmen, die in Ressourcenfiltern, Ergebnisfiltern oder bei der Ausführung von MVC-Ergebnissen auftreten.

Um eine Ausnahme zu behandeln, legen Sie die Eigenschaft ExceptionHandled auf true fest, oder weisen Sie die Eigenschaft Result zu. Dadurch wird die Weitergabe der Ausnahme beendet. Ein Ausnahmefilter kann eine Ausnahme nicht in ein „erfolgreiches Ergebnis“ umwandeln. Nur ein Aktionsfilter kann das.

Für Ausnahmefilter gilt Folgendes:

  • Sie eignen sich für das Abfangen von Ausnahmen, die in Aktionen auftreten.
  • Sie sind im Vergleich zu Middleware für die Fehlerbehandlung weniger flexibel.

Sie sollten bei der Ausnahmebehandlung vorzugsweise Middleware verwenden. Verwenden Sie Ausnahmefilter nur, wenn sich die Fehlerbehandlung unterscheidet, je nachdem, welche Aktionsmethode aufgerufen wird. Zum Beispiel verfügt eine App möglicherweise über Aktionsmethoden für beide API-Endpunkte und für Ansichten bzw. HTML. Die API-Endpunkte können Fehlerinformationen als JSON zurückgeben, während ansichtsbasierte Aktionen eine Fehlerseite als HTML zurückgeben können.

Ergebnisfilter

Für Ergebnisfilter gilt:

IResultFilter und IAsyncResultFilter

Der folgende Code zeigt einen Ergebnisfilter, der einen HTTP-Header hinzufügt:

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");
    }
}

Die Art des Ergebnisses, das ausgeführt wird, hängt von der jeweiligen Aktion ab. Eine Aktion, die eine Ansicht zurückgibt, beinhaltet die gesamte Razor-Verarbeitung als Teil der Ausführung von ViewResult. Eine API-Methode führt möglicherweise eine Art Serialisierung als Teil der Ausführung des Ergebnisses durch. Erfahren Sie mehr zu Aktionsergebnissen.

Ergebnisfilter werden nur ausgeführt, wenn eine Aktion oder ein Aktionsfilter ein Aktionsergebnis liefert. Ergebnisfilter werden in folgenden Fällen nicht ausgeführt:

  • Ein Autorisierungs- oder Ressourcenfilter schließt die Pipeline kurz.
  • Ein Ausnahmefilter behandelt eine Ausnahme durch Erzeugen eines Aktionsergebnisses.

Die Methode Microsoft.AspNetCore.Mvc.Filters.IResultFilter.OnResultExecuting kann die Ausführung des Aktionsergebnisses und der nachfolgenden Ergebnisfilter durch Festlegen von Microsoft.AspNetCore.Mvc.Filters.ResultExecutingContext.Cancel auf true kurzschließen. Schreiben Sie beim Kurzschließen in das Antwortobjekt, um zu verhindern, dass eine leere Antwort generiert wird. Das Auslösen einer Ausnahme in IResultFilter.OnResultExecuting bewirkt Folgendes:

  • Die Ausführung des Aktionsergebnisses und der nachfolgenden Filter wird verhindert.
  • Die ausgelöste Ausnahme wird nicht als erfolgreiches Ergebnis, sondern als Fehler behandelt.

Wenn die Microsoft.AspNetCore.Mvc.Filters.IResultFilter.OnResultExecuted-Methode ausgeführt wird, wurde die Antwort wahrscheinlich bereits an den Client gesendet. Wenn die Antwort bereits an den Client gesendet wurde, kann sie nicht geändert werden.

ResultExecutedContext.Canceled wird auf true festgelegt, wenn die Ausführung des Aktionsergebnisses durch einen anderen Filter kurzgeschlossen wurde.

ResultExecutedContext.Exception wird auf einen Wert ungleich NULL festgelegt, wenn das Aktionsergebnis oder ein nachfolgender Ergebnisfilter eine Ausnahme ausgelöst hat. Das Festlegen von Exception auf NULL behandelt eine Ausnahme und verhindert, dass die Ausnahme zu einem späteren Zeitpunkt in der Pipeline ein weiteres Mal ausgelöst wird. Beim Behandeln einer Ausnahme in einem Ergebnisfilter gibt es keine zuverlässige Möglichkeit, Daten in eine Antwort zu schreiben. Wenn ein Aktionsergebnis eine Ausnahme auslöst und die Header bereits an den Client übergeben wurden, gibt es keinen zuverlässigen Mechanismus, einen Fehlercode zu senden.

Bei einem IAsyncResultFilter führt ein Aufruf von await next im ResultExecutionDelegate alle nachfolgenden Ergebnisfilter und das Aktionsergebnis aus. Legen Sie ResultExecutingContext.Cancel auf true fest, und rufen Sie nicht ResultExecutionDelegate auf, um die Pipeline kurzzuschließen.

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

    }
}

Das Framework stellt ein abstraktes ResultFilterAttribute bereit, das als Unterklasse verwendet werden kann. Die weiter oben gezeigte Klasse AddHeaderAttribute ist ein Beispiel für ein Ergebnisfilterattribut.

IAlwaysRunResultFilter und IAsyncAlwaysRunResultFilter

Die Schnittstellen IAlwaysRunResultFilter und IAsyncAlwaysRunResultFilter deklarieren eine IResultFilter-Implementierung, die für alle Aktionsergebnisse ausgeführt wird. Dies schließt Aktionsergebnisse ein, die von folgenden Filtern generiert werden:

  • Autorisierungs- und Ressourcenfilter, die einen Kurzschluss bewirken
  • Ausnahmefilter

Beispielsweise wird der folgende Filter immer ausgeführt und legt ein Aktionsergebnis (ObjectResult) mit dem Statuscode 422 – Einheit kann nicht bearbeitet werden fest, wenn bei der Inhaltsaushandlung ein Fehler auftritt:

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 implementiert IFilterMetadata. Deshalb kann eine IFilterFactory-Instanz an einer beliebigen Stelle in der Filterpipeline als IFilterMetadata-Instanz verwendet werden. Wenn die Runtime den Aufruf des Filters vorbereitet, versucht sie, ihn in eine IFilterFactory umzuwandeln. Wenn diese Umwandlung gelingt, wird die Methode CreateInstance aufgerufen, um die Instanz IFilterMetadata für den Aufruf zu erstellen. Da die exakte Filterpipeline beim Start der Anwendung nicht explizit festgelegt werden muss, wird dadurch ein sehr flexibles Design ermöglicht.

IFilterFactory.IsReusable:

  • Ist ein Hinweis der Factory darauf, dass die von der Factory erstellte Filterinstanz möglicherweise außerhalb des Anforderungsbereichs, in dem sie erstellt wurde, wiederverwendet wird
  • Sollte nicht mit einem Filter verwendet werden, der von Diensten mit einer anderen Lebensdauer als der eines Singletons abhängig ist

Die ASP.NET Core-Runtime bietet keine Garantie für Folgendes:

  • Eine einzelne Instanz des Filters wird erstellt.
  • Der Filter wird nicht zu einem späteren Zeitpunkt vom DI-Container erneut angefordert.

Warnung

Konfigurieren Sie IFilterFactory.IsReusable nur so, dass true zurückgegeben wird, wenn die Quelle der Filter eindeutig ist, die Filter zustandslos sind und die Filter sicher in mehreren HTTP-Anforderungen verwendet werden können. Geben Sie beispielsweise keine Filter aus DI zurück, die als bereichsbezogen oder vorübergehend registriert sind, wenn IFilterFactory.IsReusabletrue zurückgibt. Als weiteres Verfahren zum Erstellen von Filtern kann IFilterFactory mithilfe von benutzerdefinierten Attributimplementierungen implementiert werden:

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;
        }
    }
}

Der Filter wird im folgenden Code angewendet:

[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.");
    }
}

Testen Sie den vorangehenden Code durch Ausführen des Downloadbeispiels:

  • Rufen Sie die F12-Entwicklungstools auf.
  • Navigieren Sie zu https://localhost:5001/Sample/HeaderWithFactory.

Die F12-Entwicklungstools zeigen folgende Antwortheader an, die vom Beispielcode hinzugefügt wurden:

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

Der oben stehende Code erstellt den Antwortheader „internal:My header“.

In einem Attribut implementierte IFilterFactory

Filter, die IFilterFactory implementieren, eignen sich in folgenden Fällen:

  • Sie erfordern kein Übergeben von Parametern.
  • Sie verfügen über Konstruktorabhängigkeiten, die durch DI erfüllt werden müssen.

TypeFilterAttribute implementiert IFilterFactory. IFilterFactory macht die CreateInstance-Methode zum Erstellen einer IFilterMetadata-Instanz verfügbar. CreateInstance lädt den angegebenen Typ aus dem Dienstecontainer (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");
        }
    }
}

Der folgende Code zeigt drei Ansätze zum Anwenden von [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");
}

Im oben stehenden Code ist das Ergänzen der Methode um [SampleActionFilter] der bevorzugte Ansatz zum Anwenden von SampleActionFilter.

Verwenden von Middleware in der Filterpipeline

Ressourcenfilter funktionieren wie Middleware, sie umschließen alle Ausführungen, die später in der Pipeline enthalten sind. Filter unterscheiden sich jedoch insofern von Middleware, dass sie Teil der Runtime sind, was bedeutet, dass sie Zugriff auf Kontext und Konstrukte haben.

Um Middleware als Filter zu verwenden, erstellen Sie einen Typ mit einer Configure-Methode, die die Middleware festlegt, die in die Filterpipeline eingefügt werden soll. Das folgende Beispiel verwendet die Lokalisierungsmiddleware, um die aktuelle Kultur einer Anforderung einzurichten:

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);
    }
}

Verwenden Sie das MiddlewareFilterAttribute zum Ausführen der Middleware:

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

Middlewarefilter werden zum selben Zeitpunkt in der Filterpipeline wie Ressourcenfilter, vor der Modellbindung und nach dem Rest der Pipeline ausgeführt.

Threadsicherheit

Wenn eine Instanz eines Filters in Add statt in Type übergeben wird, ist der Filter ein Singleton und ist nicht threadsicher.

Nächste Schritte