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:
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.
-
- 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:
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:
- 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. 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:
- ActionFilterAttribute
- ExceptionFilterAttribute
- ResultFilterAttribute
- FormatFilterAttribute
- ServiceFilterAttribute
- TypeFilterAttribute
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 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:
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 vonnext
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
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.
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:
- ServiceFilterAttribute
- TypeFilterAttribute
- IFilterFactory, im Attribut implementiert
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:
- 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 Pages. Razor Pages unterstützt IPageFilter und IAsyncPageFilter. Weitere Informationen finden Sie unter Filtermethoden für Razor Pages.
Für Aktionsfilter gilt Folgendes:
- 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 [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:
- 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 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:
- 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. 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.IsReusable
true
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:
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.
-
- 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:
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:
- 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. 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:
- ActionFilterAttribute
- ExceptionFilterAttribute
- ResultFilterAttribute
- FormatFilterAttribute
- ServiceFilterAttribute
- TypeFilterAttribute
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 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:
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 vonnext
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
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.
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:
- ServiceFilterAttribute
- TypeFilterAttribute
- IFilterFactory, im Attribut implementiert
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:
- 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 Pages. Razor Pages unterstützt IPageFilter und IAsyncPageFilter. Weitere Informationen finden Sie unter Filtermethoden für Razor Pages.
Für Aktionsfilter gilt Folgendes:
- 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 [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:
- 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 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:
- 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. 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.IsReusable
true
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.
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.
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.
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:
- 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. 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:
- 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. 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 Controllerfiltern und Razor Page-Filtern, 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 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 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.
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:
- 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 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:
- 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 Pages. Razor Pages unterstützt IPageFilter und IAsyncPageFilter. Weitere Informationen finden Sie unter Filtermethoden für Razor Pages.
Für Aktionsfilter gilt Folgendes:
- 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 [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:
- 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.
- 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:
- 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. 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.IsReusable
true
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
- 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.
Feedback
https://aka.ms/ContentUserFeedback.
Bald verfügbar: Im Laufe des Jahres 2024 werden wir GitHub-Issues stufenweise als Feedbackmechanismus für Inhalte abbauen und durch ein neues Feedbacksystem ersetzen. Weitere Informationen finden Sie unterFeedback senden und anzeigen für