Filter in ASP.NET Core
Von Kirk Larkin, Rick Anderson, Tom Dykstra und Steve Smith
Filter in ASP.NET Core zulassen, dass Code vor oder nach bestimmten Phasen in der Anforderungsverarbeitungspipeline ausgeführt werden kann.
Integrierte Filter sind für folgende Aufgaben zuständig:
- Autorisierung, die den Zugriff auf Ressourcen verhindert, für die ein Benutzer nicht autorisiert ist.
- Zwischenspeichern von Antworten, wobei die Anforderungspipeline kurzgeschlossen wird, um eine zwischengespeicherte Antwort zurückzugeben.
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 Seiten, API-Controller und Controller mit Ansichten. Filter funktionieren nicht direkt mit Razor Komponenten. 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:
Filtertypen
Jeder Filtertyp wird in einer anderen Phase der Filterpipeline ausgeführt:
-
- Führen Sie zuerst aus.
- Bestimmen Sie, ob der Benutzer für die Anforderung autorisiert ist.
- Kurzschluss der Pipeline, wenn die Anforderung nicht autorisiert ist.
-
- 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ühren Sie 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.
- Werden in Razor Pages nicht unterstützt.
-
- Führen Sie 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.
- Werden in Razor Pages nicht unterstützt.
- Kann 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ühren Sie unmittelbar vor und nach der Ausführung der Aktionsergebnisse aus.
- Führen Sie nur aus, wenn die Aktionsmethode erfolgreich ausgeführt wird.
- Sind nützlich für Logik, die die Ansichts- oder Formatierungsausführung umschließen muss.
Das folgende Diagramm zeigt, wie Filtertypen in der Filterpipeline interagieren:
Razor Seiten unterstützen Razor auch Seitenfilter, 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 ihrer Pipelinephase ausgeführt. OnActionExecuting beispielsweise wird aufgerufen, bevor die Aktionsmethode aufgerufen wird. OnActionExecuted wird aufgerufen, nachdem die Aktionsmethode Folgendes zurückgegeben hat:
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. Zum 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 vorangehenden Code verfügt der SampleAsyncActionFilter
über einen ActionExecutionDelegate, next
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:
- Synchron: IActionFilter und IResultFilter
- Asynchron: IAsyncActionFilter und IAsyncResultFilter
- IOrderedFilter
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. Wenn Sie abstrakte Klassen wie ActionFilterAttributeverwenden, überschreiben Sie nur die synchronen Methoden oder die asynchronen Methoden 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 Browserentwicklertools , um die Header zu untersuchen. Unter Antwortheader wird filter-header: Filter Value
angezeigt.
Der folgende Code gilt sowohl für ResponseHeaderAttribute
einen Controller als auch für eine Aktion:
[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:
- ActionFilterAttribute
- ExceptionFilterAttribute
- ResultFilterAttribute
- FormatFilterAttribute
- ServiceFilterAttribute
- TypeFilterAttribute
Filter können nicht auf Razor Page-Handlermethoden angewendet werden. Sie können entweder auf das Razor Seitenmodell 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 auf einem Controller oder Razor einer Seite.
- Verwenden eines Attributs für eine Controller-Aktion. Filterattribute können nicht auf Razor Pages-Handlermethoden angewendet werden.
- Global für alle Controller, Aktionen und Razor Seiten, 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 Controllerfiltern, der vor 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:
Sequenz | 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 von Controller erbt, umfasst die OnActionExecutingMethoden , OnActionExecutionAsyncund OnActionExecuted . Diese Methoden umschließen die Filter, die für eine bestimmte Aktion ausgeführt werden:
OnActionExecuting
wird vor einem der Filter der Aktion ausgeführt.OnActionExecuted
wird nach allen Filtern der Aktion ausgeführt.OnActionExecutionAsync
wird vor einem der Filter der Aktion ausgeführt. Code nach einem Aufruf, dernext
nach den Filtern der Aktion ausgeführt wird.
Die folgende ControllerFiltersController
Klasse:
- Wendet das
SampleActionFilterAttribute
([SampleActionFilter]
) auf den Controller an. - Überschreibt
OnActionExecuting
undOnActionExecuted
:
[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.
Informationen Razor zu Pages finden Sie unter Implementieren Razor von Seitenfiltern durch Ü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 Controllerebene" verfügt über einen globalen Bereich, GlobalSampleActionFilter
sodass er vor SampleActionFilterAttribute
ausgeführt wird, der Controllerbereich hat. SampleActionFilterAttribute
Legen Sie die Reihenfolge für die erste Ausführung auf int.MinValue
fest:
[SampleActionFilter(Order = int.MinValue)]
public class ControllerFiltersController : Controller
{
// ...
}
Damit der globale Filter GlobalSampleActionFilter
zuerst ausgeführt wird, legen Sie dessen 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. Der folgende Ressourcenfilter verhindert beispielsweise, 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. Der ShortCircuitingResourceFilterAttribute
Filter:
- 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. Die 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)}");
}
Dependency Injection
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 nicht von DI bereitgestellt werden, da für Attribute ihre Konstruktorparameter angegeben werden müssen, wo sie angewendet werden.
Die folgenden Filter unterstützen von DI bereitgestellte Konstruktorabhängigkeiten:
- ServiceFilterAttribute
- TypeFilterAttribute
- IFilterFactory, im Attribut implementiert
Die obigen 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 bereits Aktionen und Frameworkereignisse.
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 verwenden ServiceFilterAttribute
, legen Sie folgendes fest ServiceFilterAttribute.IsReusable:
- 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 Singleton 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 verwenden TypeFilterAttribute
, legen Sie folgendes fest TypeFilterAttribute.IsReusable:
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
Für Ressourcenfilter gilt:
- Sie implementieren entweder die Schnittstelle IResourceFilter oder IAsyncResourceFilter.
- Die Ausführung umschließt den größten Teil der Filterpipeline.
- Nur Autorisierungsfilter werden vor Ressourcenfiltern ausgeführt.
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:
Der oben gezeigte Ressourcenfilter zum Kurzschließen.
DisableFormValueModelBindingAttribute:
- Verhindert, dass die Modellbindung auf die Formulardaten zugreift.
- Wird bei großen Dateiuploads verwendet, um zu verhindern, dass die Formulardaten in den Arbeitsspeicher eingelesen werden.
Aktionsfilter
Aktionsfilter gelten nicht für Razor Seiten. Razor Pages unterstützt IPageFilter und IAsyncPageFilter. Weitere Informationen finden Sie unter Filtermethoden für Razor Pages.
Für Aktionsfilter gilt:
- Sie implementieren entweder die Schnittstelle IActionFilter oder IAsyncActionFilter.
- Ihre Ausführung umfasst die Ausführung von Aktionsmethoden.
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 Attribut versehen sind, überprüfen den [ApiController]
Modellzustand automatisch und geben eine Antwort von 400 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:
- Sie implementieren IExceptionFilter oder IAsyncExceptionFilter.
- Sie können verwendet werden, um gängige Fehlerbehandlungsrichtlinien zu implementieren.
Der folgende Beispielausnahmefilter zeigt Details zu Ausnahmen an, die auftreten, wenn sich die App 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.
- Behandeln sie nicht behandelte Ausnahmen, die bei Razor der Erstellung von Seiten 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 ExceptionHandled -Eigenschaft auf fest, oder weisen Sie die Result -Eigenschaft zutrue
. 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 die ansichtsbasierten Aktionen eine Fehlerseite als HTML zurückgeben können.
Ergebnisfilter
Für Ergebnisfilter gilt:
- Implementieren einer Schnittstelle:
- Ihre Ausführung umfasst die Ausführung von Aktionsergebnissen.
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. Weitere Informationen 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. Um kurzschließen zu können, legen Sie auf true
festResultExecutingContext.Cancel, und rufen Sie nicht aufResultExecutionDelegate
:
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 zuvor gezeigte ResponseHeaderAttribute-Klasse 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, dass die von der Factory erstellte Filterinstanz außerhalb des Anforderungsbereichs, in dem sie erstellt wurde, wiederverwendet werden kann.
- Sollte nicht mit einem Filter verwendet werden, der von Diensten mit einer anderen Lebensdauer als Singleton 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 sie zurückgegeben wird true
, wenn die Quelle der Filter eindeutig ist, die Filter zustandslos sind und die Filter sicher für mehrere HTTP-Anforderungen verwendet werden können. Geben Sie beispielsweise keine Filter von DI zurück, die als bereichs- oder vorübergehend registriert sind, wenn IFilterFactory.IsReusable
zurückgegeben true
wird.
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 vorherigen 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 an Add
übergeben wird, ist der Type
Filter ein Singleton und 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: Verhindern des Zugriffs auf Ressourcen, für die ein Benutzer nicht autorisiert ist.
- Zwischenspeichern der Antwort, kurzschließend die Anforderungspipeline, um eine zwischengespeicherte Antwort zurückzugeben.
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 Seiten, API-Controller und Controller mit Ansichten. Filter funktionieren nicht direkt mit Razor Komponenten. 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.
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.
-
- 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.
-
- 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.
- 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.
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. Zum 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:
- Synchron: IActionFilter und IResultFilter
- Asynchron: IAsyncActionFilter und IAsyncResultFilter
- IOrderedFilter
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. Wenn Sie abstrakte Klassen wie ActionFilterAttributeverwenden, überschreiben Sie nur die synchronen Methoden oder die asynchronen Methoden 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 Browserentwicklertools , 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 wird angezeigt, author: Rick Anderson
und Editor: Joe Smith
wird angezeigt, wenn der Sample/Index2
Endpunkt aufgerufen wird.
Der folgende Code wendet das MyActionFilterAttribute
und auf AddHeaderAttribute
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 Seitenmodell oder global angewendet werden.
Einige der Filterschnittstellen besitzen entsprechende Attribute, die als Basisklassen für benutzerdefinierte Implementierungen verwendet werden können.
Filterattribute:
- ActionFilterAttribute
- ExceptionFilterAttribute
- ResultFilterAttribute
- FormatFilterAttribute
- ServiceFilterAttribute
- TypeFilterAttribute
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. Filterattribute können nicht auf Razor Pages-Handlermethoden angewendet werden.
- Verwenden eines Attributs für einen Controller oder Razor eine Seite.
- Global für alle Controller, Aktionen und Razor Seiten, 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 Vorcode von Controller- und Razor Seitenfiltern.
- 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 Nach-Code von Controller- und Razor Seitenfiltern.
- Der Vorcode von Controller- und Razor Seitenfiltern.
- 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.
Sequenz | 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 Controller Basisklasse erbt, umfasst Controller.OnActionExecutingMethoden , Controller.OnActionExecutionAsyncund 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 nachnext
wird nach der Aktionsmethode ausgeführt.
Im Downloadbeispiel wird MySampleActionFilter
global beim Start angewendet.
Die TestController
:
- Wendet das
SampleActionFilterAttribute
([SampleActionFilter]
) auf die AktionFilterTest2
an. - Überschreibt
OnActionExecuting
undOnActionExecuted
:
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.
Informationen Razor zu Pages finden Sie unter Implementieren Razor von Seitenfiltern durch Ü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.");
}
}
Dependency Injection
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:
- ServiceFilterAttribute
- TypeFilterAttribute
- IFilterFactory, im Attribut implementiert
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 verwenden ServiceFilterAttribute
, legen Sie folgendes fest ServiceFilterAttribute.IsReusable:
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 verwenden TypeFilterAttribute
, legen Sie folgendes fest TypeFilterAttribute.IsReusable:
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
Für Ressourcenfilter gilt:
- Sie implementieren entweder die Schnittstelle IResourceFilter oder IAsyncResourceFilter.
- Die Ausführung umschließt den größten Teil der Filterpipeline.
- Nur Autorisierungsfilter werden vor Ressourcenfiltern ausgeführt.
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:
Der oben gezeigte Ressourcenfilter zum Kurzschließen.
DisableFormValueModelBindingAttribute:
- Verhindert, dass die Modellbindung auf die Formulardaten zugreift.
- Wird bei großen Dateiuploads verwendet, um zu verhindern, dass die Formulardaten in den Arbeitsspeicher eingelesen werden.
Aktionsfilter
Aktionsfilter gelten nicht für Razor Seiten. Razor Pages unterstützt IPageFilter und IAsyncPageFilter. Weitere Informationen finden Sie unter Filtermethoden für Razor Pages.
Für Aktionsfilter gilt:
- Sie implementieren entweder die Schnittstelle IActionFilter oder IAsyncActionFilter.
- Ihre Ausführung umfasst die Ausführung von Aktionsmethoden.
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 Attribut versehen sind, überprüfen den [ApiController]
Modellzustand automatisch und geben eine Antwort von 400 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:
- Sie implementieren IExceptionFilter oder IAsyncExceptionFilter.
- Sie können verwendet werden, um gängige Fehlerbehandlungsrichtlinien zu implementieren.
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.
- Behandeln sie nicht behandelte Ausnahmen, die bei Razor der Erstellung von Seiten 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 ExceptionHandled -Eigenschaft auf fest, oder weisen Sie die Result -Eigenschaft zutrue
. 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 die ansichtsbasierten Aktionen eine Fehlerseite als HTML zurückgeben können.
Ergebnisfilter
Für Ergebnisfilter gilt:
- Implementieren einer Schnittstelle:
- Ihre Ausführung umfasst die Ausführung von Aktionsergebnissen.
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. Weitere Informationen 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. Um kurzschließen zu können, legen Sie auf true
festResultExecutingContext.Cancel, und rufen Sie nicht aufResultExecutionDelegate
:
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, dass die von der Factory erstellte Filterinstanz außerhalb des Anforderungsbereichs, in dem sie erstellt wurde, wiederverwendet werden kann.
- Sollte nicht mit einem Filter verwendet werden, der von Diensten mit einer anderen Lebensdauer als Singleton abhängt.
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, um zurückzugeben true
, wenn die Quelle der Filter eindeutig ist, die Filter zustandslos sind und die Filter für mehrere HTTP-Anforderungen sicher verwendet werden können. Geben Sie beispielsweise keine Filter von DI zurück, die als bereichs- oder vorübergehend registriert sind, wenn IFilterFactory.IsReusable
zurückgegeben true
wird.
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:
- Autor:
Rick Anderson
- globaladdheader:
Result filter added to MvcOptions.Filters
- Interne:
My header
Der vorangehende Code erstellt den Internen:My header
-Antwortheader.
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
Beim Übergeben einer Instanz eines Filters an Add
anstelle von Type
an ist der Filter ein Singleton und nicht threadsicher.
Nächste Schritte
- Weitere Informationen finden Sie unter Filtermethoden für Razor Pages.
- Um Filter näher kennenzulernen, laden Sie das GitHub-Beispiel herunter, und testen und bearbeiten Sie es.