Filtry w ASP.NET Core
Przez Kirk Larkin, Rick Anderson, Tom Dykstra i Steve Smith
Filtry w ASP.NET Core umożliwiają uruchamianie kodu przed lub po określonych etapach w potoku przetwarzania żądań.
Wbudowane filtry obsługują zadania, takie jak:
- Autoryzacja, uniemożliwiająca dostęp do zasobów, dla których użytkownik nie jest autoryzowany.
- Buforowanie odpowiedzi, zwarcie potoku żądania w celu zwrócenia buforowanej odpowiedzi.
Filtry niestandardowe można tworzyć w celu obsługi problemów związanych z wycinaniami krzyżowymi. Przykłady problemów obejmujących krzyżowe obejmują obsługę błędów, buforowanie, konfigurację, autoryzację i rejestrowanie. Filtry unikają duplikowania kodu. Na przykład filtr wyjątków obsługi błędów może skonsolidować obsługę błędów.
Ten dokument dotyczy Razor stron, kontrolerów interfejsu API i kontrolerów z widokami. Filtry nie działają bezpośrednio ze składnikamiRazor. Filtr może mieć wpływ tylko pośrednio na składnik, gdy:
- Składnik jest osadzony na stronie lub w widoku.
- Strona lub kontroler i widok używają filtru.
Jak działają filtry
Filtry są uruchamiane w potoku wywołania akcji ASP.NET Core, czasami nazywane potokiem filtru. Potok filtru jest uruchamiany po wybraniu akcji do wykonania przez ASP.NET Core:
Typy filtrów
Każdy typ filtru jest wykonywany na innym etapie w potoku filtru:
-
- Uruchom najpierw polecenie .
- Ustal, czy użytkownik ma autoryzację do żądania.
- Zwarcie potoku, jeśli żądanie nie jest autoryzowane.
Filtry zasobów:
- Uruchom polecenie po autoryzacji.
- OnResourceExecuting uruchamia kod przed resztą potoku filtru. Na przykład
OnResourceExecuting
uruchamia kod przed powiązaniem modelu. - OnResourceExecuted uruchamia kod po zakończeniu pozostałej części potoku.
Filtry akcji:
- Uruchom bezpośrednio przed wywołaną metodą akcji i po jej wywołaniu.
- Może zmienić argumenty przekazane do akcji.
- Może zmienić wynik zwrócony z akcji.
- Nie są obsługiwane na Razor stronach.
-
- Uruchom bezpośrednio przed wywołaną metodą akcji i po jej wywołaniu.
- Może zmienić argumenty przekazane do akcji.
- Może zmienić wynik zwrócony z akcji.
- Nie są obsługiwane na Razor stronach.
- Można wywołać zarówno na akcjach, jak i na punktach końcowych opartych na programie obsługi tras.
Filtry wyjątków stosują zasady globalne do nieobsługiwanych wyjątków występujących przed zapisaniem treści odpowiedzi.
Filtry wyników:
- Uruchom polecenie bezpośrednio przed i po wykonaniu wyników akcji.
- Uruchom polecenie tylko wtedy, gdy metoda akcji zostanie wykonana pomyślnie.
- Są przydatne w przypadku logiki, która musi otaczać wykonywanie widoku lub formatera.
Na poniższym diagramie przedstawiono sposób interakcji typów filtrów w potoku filtru:
Razor Strony obsługują Razor również filtry strony, które są uruchamiane przed i po procedurze Razor obsługi strony.
Implementacja
Filtry obsługują zarówno implementacje synchroniczne, jak i asynchroniczne za pomocą różnych definicji interfejsu.
Filtry synchroniczne są uruchamiane przed i po etapie potoku. Na przykład OnActionExecuting jest wywoływana przed wywołaniem metody akcji. OnActionExecuted jest wywoływana po zwracaniu metody akcji:
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.
}
}
Filtry asynchroniczne definiują metodę On-Stage-ExecutionAsync
. Na przykład : 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.
}
}
W poprzednim kodzie element SampleAsyncActionFilter
ma ActionExecutionDelegatewartość , next
która wykonuje metodę akcji .
Wiele etapów filtrowania
Interfejsy dla wielu etapów filtrowania można zaimplementować w jednej klasie. Na przykład ActionFilterAttribute klasa implementuje:
- Synchroniczne: IActionFilter i IResultFilter
- Asynchroniczne: IAsyncActionFilter i IAsyncResultFilter
- IOrderedFilter
Zaimplementuj synchroniczną lub asynchroniczną wersję interfejsu filtru, a nie obie. Środowisko uruchomieniowe najpierw sprawdza, czy filtr implementuje interfejs asynchroniczny, a jeśli tak, wywołuje to. Jeśli nie, wywołuje metody interfejsu synchronicznego. Jeśli oba interfejsy asynchroniczne i synchroniczne są implementowane w jednej klasie, wywoływana jest tylko metoda asynchroniczna. W przypadku używania klas abstrakcyjnych, takich jak ActionFilterAttribute, przesłaniaj tylko metody synchroniczne lub metody asynchroniczne dla każdego typu filtru.
Wbudowane atrybuty filtru
ASP.NET Core zawiera wbudowane filtry oparte na atrybutach, które mogą być podklasowane i dostosowane. Na przykład następujący filtr wyników dodaje nagłówek do odpowiedzi:
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);
}
}
Atrybuty umożliwiają filtrowanie akceptowania argumentów, jak pokazano w poprzednim przykładzie. Zastosuj element ResponseHeaderAttribute
do kontrolera lub metody akcji i określ nazwę i wartość nagłówka HTTP:
[ResponseHeader("Filter-Header", "Filter Value")]
public class ResponseHeaderController : ControllerBase
{
public IActionResult Index() =>
Content("Examine the response headers using the F12 developer tools.");
// ...
Użyj narzędzia, takiego jak narzędzia deweloperskie przeglądarki, aby zbadać nagłówki. W obszarze Nagłówkifilter-header: Filter Value
odpowiedzi zostanie wyświetlona wartość .
Następujący kod dotyczy ResponseHeaderAttribute
zarówno kontrolera, jak i akcji:
[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.");
}
Odpowiedzi z Multiple
akcji obejmują następujące nagłówki:
filter-header: Filter Value
another-filter-header: Another Filter Value
Kilka interfejsów filtru ma odpowiednie atrybuty, których można używać jako klas bazowych na potrzeby implementacji niestandardowych.
Atrybuty filtru:
- ActionFilterAttribute
- ExceptionFilterAttribute
- ResultFilterAttribute
- FormatFilterAttribute
- ServiceFilterAttribute
- TypeFilterAttribute
Nie można zastosować filtrów do Razor metod obsługi strony. Można je zastosować do Razor modelu strony lub globalnie.
Filtrowanie zakresów i kolejności wykonywania
Filtr można dodać do potoku w jednym z trzech zakresów:
- Używanie atrybutu na kontrolerze lub Razor stronie.
- Używanie atrybutu w akcji kontrolera. Nie można zastosować atrybutów filtru do Razor metod obsługi stron.
- Globalnie dla wszystkich kontrolerów, akcji i Razor stron, jak pokazano w poniższym kodzie:
var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.AddControllersWithViews(options => { options.Filters.Add<GlobalSampleActionFilter>(); });
Domyślna kolejność wykonywania
Jeśli istnieje wiele filtrów dla określonego etapu potoku, zakres określa domyślną kolejność wykonywania filtru. Filtry globalne otaczają filtry klas, które z kolei otaczają filtry metod.
W wyniku zagnieżdżania filtrów kod po filtrach jest uruchamiany w odwrotnej kolejności przed kodem. Sekwencja filtru:
- Przed kodem filtrów globalnych.
- Przed kodem filtrów kontrolera.
- Przed kodem metody akcji filtruje.
- Po kodzie metody akcji filtruje.
- Po kodzie kontrolera filtrów.
- Przed kodem filtrów kontrolera.
- Po kodzie filtrów globalnych.
Poniższy przykład ilustruje kolejność uruchamiania metod filtrowania dla filtrów akcji synchronicznych:
Sequence | Zakres filtru | Metoda filter |
---|---|---|
1 | Globalnie | OnActionExecuting |
2 | Kontroler | OnActionExecuting |
3 | Akcja | OnActionExecuting |
100 | Akcja | OnActionExecuted |
5 | Kontroler | OnActionExecuted |
6 | Globalnie | OnActionExecuted |
Filtry na poziomie kontrolera
Każdy kontroler dziedziczony z Controller zawiera OnActionExecutingmetody , OnActionExecutionAsynci OnActionExecuted . Te metody opakowuje filtry uruchamiane dla danej akcji:
OnActionExecuting
uruchamia się przed dowolnym filtrem akcji.OnActionExecuted
uruchamia się po wszystkich filtrach akcji.OnActionExecutionAsync
uruchamia się przed dowolnym filtrem akcji. Kod po wywołaniu donext
uruchomienia po filtrach akcji.
Następująca ControllerFiltersController
klasa:
- Stosuje element
SampleActionFilterAttribute
([SampleActionFilter]
) do kontrolera. - Przesłonięcia
OnActionExecuting
iOnActionExecuted
.
[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.");
}
}
Przechodzenie do https://localhost:<port>/ControllerFilters
uruchomi następującego kodu:
ControllerFiltersController.OnActionExecuting
GlobalSampleActionFilter.OnActionExecuting
SampleActionFilterAttribute.OnActionExecuting
ControllerFiltersController.Index
SampleActionFilterAttribute.OnActionExecuted
GlobalSampleActionFilter.OnActionExecuted
ControllerFiltersController.OnActionExecuted
Filtry na poziomie kontrolera ustawiają właściwość Order na int.MinValue
. Filtry na poziomie kontrolera nie można ustawić tak, aby były uruchamiane po zastosowaniu filtrów do metod. Kolejność jest wyjaśniona w następnej sekcji.
Aby uzyskać informacje na temat Razor stron, zobacz Implementowanie Razor filtrów stron przez zastępowanie metod filtrowania.
Zastąpij kolejność domyślną
Domyślną sekwencję wykonywania można zastąpić przez zaimplementowanie metody IOrderedFilter. IOrderedFilter
Uwidacznia Order właściwość, która ma pierwszeństwo przed zakresem, aby określić kolejność wykonywania. Filtr o niższej Order
wartości:
- Uruchamia przed kodem przed filtrem o wyższej wartości
Order
. - Uruchamia po kodzie po filtrze z wyższą
Order
wartością.
W przykładzie filtry na poziomie kontrolera ma zakres globalny, GlobalSampleActionFilter
więc działa przed SampleActionFilterAttribute
, który ma zakres kontrolera. Aby najpierw wykonać SampleActionFilterAttribute
przebieg, ustaw jego kolejność na int.MinValue
:
[SampleActionFilter(Order = int.MinValue)]
public class ControllerFiltersController : Controller
{
// ...
}
Aby najpierw ustawić przebieg filtru GlobalSampleActionFilter
globalnego, ustaw wartość Order
int.MinValue
:
builder.Services.AddControllersWithViews(options =>
{
options.Filters.Add<GlobalSampleActionFilter>(int.MinValue);
});
Anulowanie i zwarcie
Potok filtru można zwarcić, ustawiając Result właściwość parametru ResourceExecutingContext podanego w metodzie filtru. Na przykład następujący filtr zasobu uniemożliwia wykonywanie pozostałej części potoku:
public class ShortCircuitingResourceFilterAttribute : Attribute, IResourceFilter
{
public void OnResourceExecuting(ResourceExecutingContext context)
{
context.Result = new ContentResult
{
Content = nameof(ShortCircuitingResourceFilterAttribute)
};
}
public void OnResourceExecuted(ResourceExecutedContext context) { }
}
W poniższym kodzie, zarówno filtr, jak [ShortCircuitingResourceFilter]
i docelowy [ResponseHeader]
Index
filtr metody akcji. Filtr ShortCircuitingResourceFilterAttribute
:
- Uruchamia najpierw, ponieważ jest to filtr zasobów i
ResponseHeaderAttribute
jest filtrem akcji. - Zwarcie pozostałej części potoku.
W związku z tym ResponseHeaderAttribute
filtr nigdy nie jest uruchamiany dla Index
akcji. To zachowanie byłoby takie samo, jeśli oba filtry zostały zastosowane na poziomie metody akcji, pod warunkiem, że uruchomiono ShortCircuitingResourceFilterAttribute
najpierw. Przebiegi ShortCircuitingResourceFilterAttribute
najpierw ze względu na jego typ filtru:
[ResponseHeader("Filter-Header", "Filter Value")]
public class ShortCircuitingController : Controller
{
[ShortCircuitingResourceFilter]
public IActionResult Index() =>
Content($"- {nameof(ShortCircuitingController)}.{nameof(Index)}");
}
Wstrzykiwanie zależności
Filtry można dodawać według typu lub wystąpienia. Jeśli wystąpienie zostanie dodane, to wystąpienie jest używane dla każdego żądania. Jeśli typ zostanie dodany, zostanie aktywowany typ. Filtr aktywowany przez typ oznacza:
- Wystąpienie jest tworzone dla każdego żądania.
- Wszystkie zależności konstruktora są wypełniane przez wstrzykiwanie zależności (DI).
Filtry implementowane jako atrybuty i dodawane bezpośrednio do klas kontrolerów lub metod akcji nie mogą mieć zależności konstruktora zapewniane przez wstrzykiwanie zależności (DI). Zależności konstruktora nie mogą być udostępniane przez di, ponieważ atrybuty muszą mieć parametry konstruktora podane tam, gdzie są stosowane.
Następujące filtry obsługują zależności konstruktora dostarczone z di:
- ServiceFilterAttribute
- TypeFilterAttribute
- IFilterFactory zaimplementowany w atrybucie .
Powyższe filtry można zastosować do kontrolera lub akcji.
Rejestratory są dostępne w witrynie DI. Należy jednak unikać tworzenia i używania filtrów wyłącznie do celów rejestrowania. Wbudowane rejestrowanie struktury zwykle zapewnia, co jest potrzebne do rejestrowania. Rejestrowanie dodane do filtrów:
- Należy skoncentrować się na problemach dotyczących domeny biznesowej lub zachowaniu specyficznym dla filtru.
- Nie należy rejestrować akcji ani innych zdarzeń struktury. Wbudowane filtry rejestrują już akcje i zdarzenia struktury.
ServiceFilterAttribute
Typy implementacji filtru usługi są rejestrowane w programie Program.cs
. Element ServiceFilterAttribute pobiera wystąpienie filtru z di.
Poniższy kod przedstawia klasę LoggingResponseHeaderFilterService
, która używa di:
public class LoggingResponseHeaderFilterService : IResultFilter
{
private readonly ILogger _logger;
public LoggingResponseHeaderFilterService(
ILogger<LoggingResponseHeaderFilterService> logger) =>
_logger = logger;
public void OnResultExecuting(ResultExecutingContext context)
{
_logger.LogInformation(
$"- {nameof(LoggingResponseHeaderFilterService)}.{nameof(OnResultExecuting)}");
context.HttpContext.Response.Headers.Add(
nameof(OnResultExecuting), nameof(LoggingResponseHeaderFilterService));
}
public void OnResultExecuted(ResultExecutedContext context)
{
_logger.LogInformation(
$"- {nameof(LoggingResponseHeaderFilterService)}.{nameof(OnResultExecuted)}");
}
}
W poniższym kodzie LoggingResponseHeaderFilterService
zostanie dodany do kontenera DI:
builder.Services.AddScoped<LoggingResponseHeaderFilterService>();
W poniższym kodzie ServiceFilter
atrybut pobiera wystąpienie filtru LoggingResponseHeaderFilterService
z di:
[ServiceFilter<LoggingResponseHeaderFilterService>]
public IActionResult WithServiceFilter() =>
Content($"- {nameof(FilterDependenciesController)}.{nameof(WithServiceFilter)}");
W przypadku używania polecenia ServiceFilterAttribute
, ustawienie ServiceFilterAttribute.IsReusable:
- Zawiera wskazówkę, że wystąpienie filtru może być ponownie używane poza zakresem żądania, w ramach którego został utworzony. Środowisko uruchomieniowe ASP.NET Core nie gwarantuje:
- Zostanie utworzone pojedyncze wystąpienie filtru.
- Filtr nie zostanie ponownie zażądany z kontenera DI w późniejszym czasie.
- Nie należy używać z filtrem, który zależy od usług z okresem istnienia innym niż pojedynczy.
ServiceFilterAttribute implementuje IFilterFactory. IFilterFactory
Uwidacznia metodę CreateInstance IFilterMetadata tworzenia wystąpienia. CreateInstance
ładuje określony typ z DI.
TypeFilterAttribute
TypeFilterAttribute jest podobny do ServiceFilterAttribute, ale jego typ nie jest rozpoznawany bezpośrednio z kontenera DI. Tworzy wystąpienie typu przy użyciu polecenia Microsoft.Extensions.DependencyInjection.ObjectFactory.
Ponieważ TypeFilterAttribute
typy nie są rozpoznawane bezpośrednio z kontenera DI:
- Typy, do których odwołuje się odwołanie przy użyciu
TypeFilterAttribute
kontenera di, nie muszą być zarejestrowane. Mają one swoje zależności spełnione przez kontener DI. TypeFilterAttribute
opcjonalnie może akceptować argumenty konstruktora dla typu.
W przypadku używania polecenia TypeFilterAttribute
, ustawienie TypeFilterAttribute.IsReusable:
Zawiera wskazówkę, że wystąpienie filtru może być ponownie używane poza zakresem żądania, w ramach którego został utworzony. Środowisko uruchomieniowe ASP.NET Core nie gwarantuje utworzenia pojedynczego wystąpienia filtru.
Nie należy używać z filtrem, który zależy od usług z okresem istnienia innym niż pojedynczy.
W poniższym przykładzie pokazano, jak przekazać argumenty do typu przy użyciu polecenia TypeFilterAttribute
:
[TypeFilter(typeof(LoggingResponseHeaderFilter),
Arguments = new object[] { "Filter-Header", "Filter Value" })]
public IActionResult WithTypeFilter() =>
Content($"- {nameof(FilterDependenciesController)}.{nameof(WithTypeFilter)}");
Filtry autoryzacji
Filtry autoryzacji:
- Czy pierwsze filtry są uruchamiane w potoku filtru.
- Kontrolowanie dostępu do metod akcji.
- Przed metodą, ale nie po metodzie.
Niestandardowe filtry autoryzacji wymagają niestandardowej struktury autoryzacji. Preferuj konfigurowanie zasad autoryzacji lub pisanie niestandardowych zasad autoryzacji w celu zapisania filtru niestandardowego. Wbudowany filtr autoryzacji:
- Wywołuje system autoryzacji.
- Nie autoryzuje żądań.
Nie zgłaszaj wyjątków w filtrach autoryzacji:
- Wyjątek nie zostanie obsłużony.
- Filtry wyjątków nie będą obsługiwać wyjątku.
Rozważ wystawienie wyzwania w przypadku wystąpienia wyjątku w filtrze autoryzacji.
Dowiedz się więcej o autoryzacji.
Filtry zasobów
Filtry zasobów:
- Zaimplementuj IResourceFilter interfejs lub IAsyncResourceFilter .
- Wykonanie opakowuje większość potoku filtru.
- Filtry autoryzacji są uruchamiane przed filtrami zasobów.
Filtry zasobów są przydatne w przypadku zwarć większość potoku. Na przykład filtr buforowania może uniknąć reszty potoku po trafieniu pamięci podręcznej.
Przykłady filtrów zasobów:
Wcześniej pokazano filtr zasobów zwarciowych.
DisableFormValueModelBindingAttribute:
- Zapobiega uzyskiwaniu dostępu do danych formularza przez powiązanie modelu.
- Służy do przekazywania dużych plików, aby zapobiec odczytywaniu danych formularza do pamięci.
Filtry akcji
Filtry akcji nie mają zastosowania do Razor stron. Razor Obsługa stron i IPageFilter IAsyncPageFilter. Aby uzyskać więcej informacji, zobacz Metody filtrowania dla Razor stron.
Filtry akcji:
- Zaimplementuj IActionFilter interfejs lub IAsyncActionFilter .
- Ich wykonywanie otacza wykonywanie metod akcji.
Poniższy kod przedstawia przykładowy filtr akcji:
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.
}
}
Element ActionExecutingContext zawiera następujące właściwości:
- ActionArguments — umożliwia odczytywanie danych wejściowych do metody akcji.
- Controller — umożliwia manipulowanie wystąpieniem kontrolera.
- Result - ustawianie
Result
zwarć wykonywania metody akcji i kolejnych filtrów akcji.
Zgłaszanie wyjątku w metodzie akcji:
- Zapobiega uruchamianiu kolejnych filtrów.
- W przeciwieństwie do ustawienia
Result
, jest traktowany jako błąd zamiast pomyślnego wyniku.
Controller
Zapewnia ActionExecutedContext i Result
oraz następujące właściwości:
- Canceled — prawda, jeśli wykonanie akcji zostało zwarcie przez inny filtr.
- Exception — Wartość inna niż null, jeśli akcja lub wcześniej uruchomiony filtr akcji zwrócił wyjątek. Ustawienie tej właściwości na null:
- Skutecznie obsługuje wyjątek.
Result
jest wykonywany tak, jakby został zwrócony z metody akcji.
W przypadku metody IAsyncActionFilter
wywołanie metody ActionExecutionDelegate:
- Wykonuje wszystkie kolejne filtry akcji i metodę akcji.
- Zwraca wartość
ActionExecutedContext
.
Aby zwarć, przypisz Microsoft.AspNetCore.Mvc.Filters.ActionExecutingContext.Result do wystąpienia wyników i nie wywołuje next
metody ().ActionExecutionDelegate
Struktura zapewnia abstrakcję ActionFilterAttribute , która może być podklasowana.
Filtr OnActionExecuting
akcji może służyć do:
- Weryfikowanie stanu modelu.
- Zwróć błąd, jeśli stan jest nieprawidłowy.
public class ValidateModelAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
if (!context.ModelState.IsValid)
{
context.Result = new BadRequestObjectResult(context.ModelState);
}
}
}
Uwaga
Kontrolery z adnotacjami z atrybutem [ApiController]
automatycznie weryfikują stan modelu i zwracają odpowiedź 400. Aby uzyskać więcej informacji, zobacz Automatyczne odpowiedzi HTTP 400.
Metoda OnActionExecuted
jest uruchamiana po metodzie akcji:
- I może wyświetlać i manipulować wynikami akcji za pośrednictwem Result właściwości .
- Canceled parametr ma wartość true, jeśli wykonanie akcji zostało zwarcie przez inny filtr.
- Exception parametr jest ustawiony na wartość inną niż null, jeśli akcja lub kolejny filtr akcji zwrócił wyjątek. Ustawienie
Exception
wartości null:- Skutecznie obsługuje wyjątek.
ActionExecutedContext.Result
jest wykonywany tak, jakby został zwrócony normalnie z metody akcji.
Filtry wyjątków
Filtry wyjątków:
- Zaimplementuj IExceptionFilter lub IAsyncExceptionFilter.
- Może służyć do implementowania typowych zasad obsługi błędów.
Poniższy przykładowy filtr wyjątku wyświetla szczegółowe informacje o wyjątkach występujących podczas opracowywania aplikacji:
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()
};
}
}
Poniższy kod testuje filtr wyjątku:
[TypeFilter<SampleExceptionFilter>]
public class ExceptionController : Controller
{
public IActionResult Index() =>
Content($"- {nameof(ExceptionController)}.{nameof(Index)}");
}
Filtry wyjątków:
- Nie mają wcześniej i po zdarzeniach.
- Zaimplementuj OnException lub OnExceptionAsync.
- Obsługa nieobsługiwanych wyjątków występujących w Razor procesie tworzenia strony lub kontrolera, powiązania modelu, filtrów akcji lub metod akcji.
- Nie przechwytuj wyjątków występujących w filtrach zasobów, filtrach wyników lub wykonywaniu wyników MVC.
Aby obsłużyć wyjątek, ustaw ExceptionHandled właściwość na true
lub przypisz Result właściwość . Spowoduje to zatrzymanie propagacji wyjątku. Filtr wyjątku nie może przekształcić wyjątku w "powodzenie". Tylko filtr akcji może to zrobić.
Filtry wyjątków:
- Są dobre w przypadku podlewek wyjątków występujących w ramach akcji.
- Nie są tak elastyczne, jak oprogramowanie pośredniczące obsługujące błędy.
Preferuj oprogramowanie pośredniczące do obsługi wyjątków. Używaj filtrów wyjątków tylko wtedy, gdy obsługa błędów różni się w zależności od wywoływanej metody akcji. Na przykład aplikacja może mieć metody akcji zarówno dla punktów końcowych interfejsu API, jak i dla widoków/HTML. Punkty końcowe interfejsu API mogą zwracać informacje o błędach jako JSWŁĄCZONE, podczas gdy akcje oparte na widoku mogą zwracać stronę błędu jako kod HTML.
Filtry wyników
Filtry wyników:
- Implementowanie interfejsu:
- Ich wykonanie otacza wykonywanie wyników akcji.
IResultFilter i IAsyncResultFilter
Poniższy kod przedstawia przykładowy filtr wyników:
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.
}
}
Rodzaj wykonywanego wyniku zależy od akcji. Akcja zwracająca widok obejmuje wszystkie przetwarzanie razor w ramach wykonywanego ViewResult elementu. Metoda interfejsu API może wykonać serializacji w ramach wykonywania wyniku. Dowiedz się więcej o wynikach akcji.
Filtry wyników są wykonywane tylko wtedy, gdy akcja lub filtr akcji generuje wynik akcji. Filtry wyników nie są wykonywane, gdy:
- Filtr autoryzacji lub filtr zasobów zwarcie potoku.
- Filtr wyjątku obsługuje wyjątek, generując wynik akcji.
Metoda Microsoft.AspNetCore.Mvc.Filters.IResultFilter.OnResultExecuting może zwarć wykonywanie wyniku akcji i kolejnych filtrów wyników, ustawiając wartość Microsoft.AspNetCore.Mvc.Filters.ResultExecutingContext.Cancel true
. Zapisz w obiekcie odpowiedzi podczas zwarć, aby uniknąć generowania pustej odpowiedzi. Zgłaszanie wyjątku w pliku IResultFilter.OnResultExecuting
:
- Zapobiega wykonywaniu wyniku akcji i kolejnych filtrów.
- Jest traktowany jako błąd zamiast pomyślnego wyniku.
Po uruchomieniu Microsoft.AspNetCore.Mvc.Filters.IResultFilter.OnResultExecuted metody odpowiedź prawdopodobnie została już wysłana do klienta. Jeśli odpowiedź została już wysłana do klienta, nie można jej zmienić.
ResultExecutedContext.Canceled
parametr jest ustawiony na true
wartość , jeśli wykonanie wyniku akcji zostało zwarcie przez inny filtr.
ResultExecutedContext.Exception
parametr jest ustawiony na wartość inną niż null, jeśli wynik akcji lub kolejny filtr wyników zwrócił wyjątek. Ustawienie Exception
wartości null skutecznie obsługuje wyjątek i uniemożliwia ponowne zgłoszenie wyjątku w dalszej części potoku. Nie ma niezawodnego sposobu zapisywania danych w odpowiedzi podczas obsługi wyjątku w filtrze wyników. Jeśli nagłówki zostały opróżnione do klienta, gdy wynik akcji zgłasza wyjątek, nie ma niezawodnego mechanizmu wysyłania kodu błędu.
W przypadku elementu IAsyncResultFilterwywołanie metody await next
na obiekcie ResultExecutionDelegate wykonuje wszystkie kolejne filtry wyników i wynik akcji. Aby zwarć, ustaw wartość ResultExecutingContext.Cancel true
i nie wywołaj metody ResultExecutionDelegate
:
public class SampleAsyncResultFilter : IAsyncResultFilter
{
public async Task OnResultExecutionAsync(
ResultExecutingContext context, ResultExecutionDelegate next)
{
if (context.Result is not EmptyResult)
{
await next();
}
else
{
context.Cancel = true;
}
}
}
Struktura zapewnia abstrakcję ResultFilterAttribute
, która może być podklasowana. Pokazana wcześniej klasa ResponseHeaderAttribute jest przykładem atrybutu filtru wyników.
IAlwaysRunResultFilter i IAsyncAlwaysRunResultFilter
Interfejsy IAlwaysRunResultFilter i IAsyncAlwaysRunResultFilter deklarują implementację uruchamianą IResultFilter dla wszystkich wyników akcji. Obejmuje to wyniki akcji wygenerowane przez:
- Filtry autoryzacji i filtry zasobów, które są zwarcie.
- Filtry wyjątków.
Na przykład następujący filtr zawsze jest uruchamiany i ustawia wynik akcji (ObjectResult) z kodem stanu jednostki nieprzetworzonej 422, gdy negocjowanie zawartości kończy się niepowodzeniem:
public class UnprocessableResultFilter : IAlwaysRunResultFilter
{
public void OnResultExecuting(ResultExecutingContext context)
{
if (context.Result is StatusCodeResult statusCodeResult
&& statusCodeResult.StatusCode == StatusCodes.Status415UnsupportedMediaType)
{
context.Result = new ObjectResult("Unprocessable")
{
StatusCode = StatusCodes.Status422UnprocessableEntity
};
}
}
public void OnResultExecuted(ResultExecutedContext context) { }
}
IFilterFactory
IFilterFactory implementuje IFilterMetadata. IFilterFactory
W związku z tym wystąpienie może być używane jako IFilterMetadata
wystąpienie w dowolnym miejscu w potoku filtru. Gdy środowisko uruchomieniowe przygotowuje się do wywołania filtru, próbuje rzutować go do elementu IFilterFactory
. Jeśli rzutowanie powiedzie się, metoda jest wywoływana w CreateInstance celu utworzenia wywoływanego IFilterMetadata
wystąpienia. Zapewnia to elastyczny projekt, ponieważ dokładny potok filtru nie musi być jawnie ustawiany podczas uruchamiania aplikacji.
IFilterFactory.IsReusable
:
- Jest wskazówką fabryki, że wystąpienie filtru utworzone przez fabrykę może być ponownie używane poza zakresem żądania, w ramach którego został utworzony.
- Nie należy używać z filtrem, który zależy od usług z okresem istnienia innym niż pojedynczy.
Środowisko uruchomieniowe ASP.NET Core nie gwarantuje:
- Zostanie utworzone pojedyncze wystąpienie filtru.
- Filtr nie zostanie ponownie zażądany z kontenera DI w późniejszym czasie.
Ostrzeżenie
Skonfiguruj wartość tak, aby zwracać IFilterFactory.IsReusable true
tylko wtedy, gdy źródło filtrów jest jednoznaczne, filtry są bezstanowe, a filtry są bezpieczne do użycia w wielu żądaniach HTTP. Na przykład nie zwracaj filtrów z di, które są zarejestrowane jako zakres lub przejściowe, jeśli IFilterFactory.IsReusable
zwraca wartość true
.
IFilterFactory
można zaimplementować przy użyciu implementacji atrybutów niestandardowych jako innego podejścia do tworzenia filtrów:
public class ResponseHeaderFilterFactory : Attribute, IFilterFactory
{
public bool IsReusable => false;
public IFilterMetadata CreateInstance(IServiceProvider serviceProvider) =>
new InternalResponseHeaderFilter();
private class InternalResponseHeaderFilter : IActionFilter
{
public void OnActionExecuting(ActionExecutingContext context) =>
context.HttpContext.Response.Headers.Add(
nameof(OnActionExecuting), nameof(InternalResponseHeaderFilter));
public void OnActionExecuted(ActionExecutedContext context) { }
}
Filtr jest stosowany w następującym kodzie:
[ResponseHeaderFilterFactory]
public IActionResult Index() =>
Content($"- {nameof(FilterFactoryController)}.{nameof(Index)}");
IFilterFactory zaimplementowany w atrybucie
Filtry implementujące IFilterFactory
są przydatne w przypadku filtrów, które:
- Nie wymagaj przekazywania parametrów.
- Mieć zależności konstruktora, które muszą być wypełnione przez di.
TypeFilterAttribute implementuje IFilterFactory. IFilterFactory
Uwidacznia metodę CreateInstance IFilterMetadata tworzenia wystąpienia. CreateInstance
ładuje określony typ z kontenera usług (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)}");
}
}
}
Poniższy kod przedstawia trzy podejścia do zastosowania filtru:
[SampleActionTypeFilter]
public IActionResult WithDirectAttribute() =>
Content($"- {nameof(FilterFactoryController)}.{nameof(WithDirectAttribute)}");
[TypeFilter<SampleActionTypeFilterAttribute>]
public IActionResult WithTypeFilterAttribute() =>
Content($"- {nameof(FilterFactoryController)}.{nameof(WithTypeFilterAttribute)}");
[ServiceFilter<SampleActionTypeFilterAttribute>]
public IActionResult WithServiceFilterAttribute() =>
Content($"- {nameof(FilterFactoryController)}.{nameof(WithServiceFilterAttribute)}");
W poprzednim kodzie preferowane jest pierwsze podejście do zastosowania filtru.
Używanie oprogramowania pośredniczącego w potoku filtru
Filtry zasobów działają jak oprogramowanie pośredniczące, które otaczają wykonywanie wszystkich elementów, które są dostępne później w potoku. Jednak filtry różnią się od oprogramowania pośredniczącego, ponieważ są one częścią środowiska uruchomieniowego, co oznacza, że mają dostęp do kontekstu i konstrukcji.
Aby użyć oprogramowania pośredniczącego jako filtru, utwórz typ z metodą określającą Configure
oprogramowanie pośredniczące do wstrzykiwania do potoku filtru. W poniższym przykładzie użyto oprogramowania pośredniczącego do ustawienia nagłówka odpowiedzi:
public class FilterMiddlewarePipeline
{
public void Configure(IApplicationBuilder app)
{
app.Use(async (context, next) =>
{
context.Response.Headers.Add("Pipeline", "Middleware");
await next();
});
}
}
Użyj polecenia , MiddlewareFilterAttribute aby uruchomić oprogramowanie pośredniczące:
[MiddlewareFilter<FilterMiddlewarePipeline>]
public class FilterMiddlewareController : Controller
{
public IActionResult Index() =>
Content($"- {nameof(FilterMiddlewareController)}.{nameof(Index)}");
}
Filtry oprogramowania pośredniczącego są uruchamiane na tym samym etapie potoku filtru co filtry zasobów, przed powiązaniem modelu i po pozostałej części potoku.
Bezpieczeństwo wątkowe
Podczas przekazywania wystąpienia filtru do Add
elementu , a nie do Type
elementu , filtr jest pojedynczym elementem i nie jest bezpieczny wątkowo.
Dodatkowe zasoby
Przez Kirk Larkin, Rick Anderson, Tom Dykstra i Steve Smith
Filtry w ASP.NET Core umożliwiają uruchamianie kodu przed lub po określonych etapach w potoku przetwarzania żądań.
Wbudowane filtry obsługują zadania, takie jak:
- Autoryzacja, uniemożliwiająca dostęp do zasobów, dla których użytkownik nie jest autoryzowany.
- Buforowanie odpowiedzi, zwarcie potoku żądania w celu zwrócenia buforowanej odpowiedzi.
Filtry niestandardowe można tworzyć w celu obsługi problemów związanych z wycinaniami krzyżowymi. Przykłady problemów obejmujących krzyżowe obejmują obsługę błędów, buforowanie, konfigurację, autoryzację i rejestrowanie. Filtry unikają duplikowania kodu. Na przykład filtr wyjątków obsługi błędów może skonsolidować obsługę błędów.
Ten dokument dotyczy Razor stron, kontrolerów interfejsu API i kontrolerów z widokami. Filtry nie działają bezpośrednio ze składnikamiRazor. Filtr może mieć wpływ tylko pośrednio na składnik, gdy:
- Składnik jest osadzony na stronie lub w widoku.
- Strona lub kontroler i widok używają filtru.
Jak działają filtry
Filtry są uruchamiane w potoku wywołania akcji ASP.NET Core, czasami nazywane potokiem filtru. Potok filtru jest uruchamiany po wybraniu akcji do wykonania przez ASP.NET Core:
Typy filtrów
Każdy typ filtru jest wykonywany na innym etapie w potoku filtru:
-
- Uruchom najpierw polecenie .
- Ustal, czy użytkownik ma autoryzację do żądania.
- Zwarcie potoku, jeśli żądanie nie jest autoryzowane.
Filtry zasobów:
- Uruchom polecenie po autoryzacji.
- OnResourceExecuting uruchamia kod przed resztą potoku filtru. Na przykład
OnResourceExecuting
uruchamia kod przed powiązaniem modelu. - OnResourceExecuted uruchamia kod po zakończeniu pozostałej części potoku.
Filtry akcji:
- Uruchom bezpośrednio przed wywołaną metodą akcji i po jej wywołaniu.
- Może zmienić argumenty przekazane do akcji.
- Może zmienić wynik zwrócony z akcji.
- Nie są obsługiwane na Razor stronach.
-
- Uruchom bezpośrednio przed wywołaną metodą akcji i po jej wywołaniu.
- Może zmienić argumenty przekazane do akcji.
- Może zmienić wynik zwrócony z akcji.
- Nie są obsługiwane na Razor stronach.
- Można wywołać zarówno na akcjach, jak i na punktach końcowych opartych na programie obsługi tras.
Filtry wyjątków stosują zasady globalne do nieobsługiwanych wyjątków występujących przed zapisaniem treści odpowiedzi.
Filtry wyników:
- Uruchom polecenie bezpośrednio przed i po wykonaniu wyników akcji.
- Uruchom polecenie tylko wtedy, gdy metoda akcji zostanie wykonana pomyślnie.
- Są przydatne w przypadku logiki, która musi otaczać wykonywanie widoku lub formatera.
Na poniższym diagramie przedstawiono sposób interakcji typów filtrów w potoku filtru:
Razor Strony obsługują Razor również filtry strony, które są uruchamiane przed i po procedurze Razor obsługi strony.
Implementacja
Filtry obsługują zarówno implementacje synchroniczne, jak i asynchroniczne za pomocą różnych definicji interfejsu.
Filtry synchroniczne są uruchamiane przed i po etapie potoku. Na przykład OnActionExecuting jest wywoływana przed wywołaniem metody akcji. OnActionExecuted jest wywoływana po zwracaniu metody akcji:
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.
}
}
Filtry asynchroniczne definiują metodę On-Stage-ExecutionAsync
. Na przykład : 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.
}
}
W poprzednim kodzie element SampleAsyncActionFilter
ma ActionExecutionDelegatewartość , next
która wykonuje metodę akcji .
Wiele etapów filtrowania
Interfejsy dla wielu etapów filtrowania można zaimplementować w jednej klasie. Na przykład ActionFilterAttribute klasa implementuje:
- Synchroniczne: IActionFilter i IResultFilter
- Asynchroniczne: IAsyncActionFilter i IAsyncResultFilter
- IOrderedFilter
Zaimplementuj synchroniczną lub asynchroniczną wersję interfejsu filtru, a nie obie. Środowisko uruchomieniowe najpierw sprawdza, czy filtr implementuje interfejs asynchroniczny, a jeśli tak, wywołuje to. Jeśli nie, wywołuje metody interfejsu synchronicznego. Jeśli oba interfejsy asynchroniczne i synchroniczne są implementowane w jednej klasie, wywoływana jest tylko metoda asynchroniczna. W przypadku używania klas abstrakcyjnych, takich jak ActionFilterAttribute, przesłaniaj tylko metody synchroniczne lub metody asynchroniczne dla każdego typu filtru.
Wbudowane atrybuty filtru
ASP.NET Core zawiera wbudowane filtry oparte na atrybutach, które mogą być podklasowane i dostosowane. Na przykład następujący filtr wyników dodaje nagłówek do odpowiedzi:
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);
}
}
Atrybuty umożliwiają filtrowanie akceptowania argumentów, jak pokazano w poprzednim przykładzie. Zastosuj element ResponseHeaderAttribute
do kontrolera lub metody akcji i określ nazwę i wartość nagłówka HTTP:
[ResponseHeader("Filter-Header", "Filter Value")]
public class ResponseHeaderController : ControllerBase
{
public IActionResult Index() =>
Content("Examine the response headers using the F12 developer tools.");
// ...
Użyj narzędzia, takiego jak narzędzia deweloperskie przeglądarki, aby zbadać nagłówki. W obszarze Nagłówkifilter-header: Filter Value
odpowiedzi zostanie wyświetlona wartość .
Następujący kod dotyczy ResponseHeaderAttribute
zarówno kontrolera, jak i akcji:
[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.");
}
Odpowiedzi z Multiple
akcji obejmują następujące nagłówki:
filter-header: Filter Value
another-filter-header: Another Filter Value
Kilka interfejsów filtru ma odpowiednie atrybuty, których można używać jako klas bazowych na potrzeby implementacji niestandardowych.
Atrybuty filtru:
- ActionFilterAttribute
- ExceptionFilterAttribute
- ResultFilterAttribute
- FormatFilterAttribute
- ServiceFilterAttribute
- TypeFilterAttribute
Nie można zastosować filtrów do Razor metod obsługi strony. Można je zastosować do Razor modelu strony lub globalnie.
Filtrowanie zakresów i kolejności wykonywania
Filtr można dodać do potoku w jednym z trzech zakresów:
- Używanie atrybutu na kontrolerze lub Razor stronie.
- Używanie atrybutu w akcji kontrolera. Nie można zastosować atrybutów filtru do Razor metod obsługi stron.
- Globalnie dla wszystkich kontrolerów, akcji i Razor stron, jak pokazano w poniższym kodzie:
var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.AddControllersWithViews(options => { options.Filters.Add<GlobalSampleActionFilter>(); });
Domyślna kolejność wykonywania
Jeśli istnieje wiele filtrów dla określonego etapu potoku, zakres określa domyślną kolejność wykonywania filtru. Filtry globalne otaczają filtry klas, które z kolei otaczają filtry metod.
W wyniku zagnieżdżania filtrów kod po filtrach jest uruchamiany w odwrotnej kolejności przed kodem. Sekwencja filtru:
- Przed kodem filtrów globalnych.
- Przed kodem filtrów kontrolera.
- Przed kodem metody akcji filtruje.
- Po kodzie metody akcji filtruje.
- Po kodzie kontrolera filtrów.
- Przed kodem filtrów kontrolera.
- Po kodzie filtrów globalnych.
Poniższy przykład ilustruje kolejność uruchamiania metod filtrowania dla filtrów akcji synchronicznych:
Sequence | Zakres filtru | Metoda filter |
---|---|---|
1 | Globalnie | OnActionExecuting |
2 | Kontroler | OnActionExecuting |
3 | Akcja | OnActionExecuting |
100 | Akcja | OnActionExecuted |
5 | Kontroler | OnActionExecuted |
6 | Globalnie | OnActionExecuted |
Filtry na poziomie kontrolera
Każdy kontroler dziedziczony z Controller zawiera OnActionExecutingmetody , OnActionExecutionAsynci OnActionExecuted . Te metody opakowuje filtry uruchamiane dla danej akcji:
OnActionExecuting
uruchamia się przed dowolnym filtrem akcji.OnActionExecuted
uruchamia się po wszystkich filtrach akcji.OnActionExecutionAsync
uruchamia się przed dowolnym filtrem akcji. Kod po wywołaniu donext
uruchomienia po filtrach akcji.
Następująca ControllerFiltersController
klasa:
- Stosuje element
SampleActionFilterAttribute
([SampleActionFilter]
) do kontrolera. - Przesłonięcia
OnActionExecuting
iOnActionExecuted
.
[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.");
}
}
Przechodzenie do https://localhost:<port>/ControllerFilters
uruchomi następującego kodu:
ControllerFiltersController.OnActionExecuting
GlobalSampleActionFilter.OnActionExecuting
SampleActionFilterAttribute.OnActionExecuting
ControllerFiltersController.Index
SampleActionFilterAttribute.OnActionExecuted
GlobalSampleActionFilter.OnActionExecuted
ControllerFiltersController.OnActionExecuted
Filtry na poziomie kontrolera ustawiają właściwość Order na int.MinValue
. Filtry na poziomie kontrolera nie można ustawić tak, aby były uruchamiane po zastosowaniu filtrów do metod. Kolejność jest wyjaśniona w następnej sekcji.
Aby uzyskać informacje na temat Razor stron, zobacz Implementowanie Razor filtrów stron przez zastępowanie metod filtrowania.
Zastąpij kolejność domyślną
Domyślną sekwencję wykonywania można zastąpić przez zaimplementowanie metody IOrderedFilter. IOrderedFilter
Uwidacznia Order właściwość, która ma pierwszeństwo przed zakresem, aby określić kolejność wykonywania. Filtr o niższej Order
wartości:
- Uruchamia przed kodem przed filtrem o wyższej wartości
Order
. - Uruchamia po kodzie po filtrze z wyższą
Order
wartością.
W przykładzie filtry na poziomie kontrolera ma zakres globalny, GlobalSampleActionFilter
więc działa przed SampleActionFilterAttribute
, który ma zakres kontrolera. Aby najpierw wykonać SampleActionFilterAttribute
przebieg, ustaw jego kolejność na int.MinValue
:
[SampleActionFilter(Order = int.MinValue)]
public class ControllerFiltersController : Controller
{
// ...
}
Aby najpierw ustawić przebieg filtru GlobalSampleActionFilter
globalnego, ustaw wartość Order
int.MinValue
:
builder.Services.AddControllersWithViews(options =>
{
options.Filters.Add<GlobalSampleActionFilter>(int.MinValue);
});
Anulowanie i zwarcie
Potok filtru można zwarcić, ustawiając Result właściwość parametru ResourceExecutingContext podanego w metodzie filtru. Na przykład następujący filtr zasobu uniemożliwia wykonywanie pozostałej części potoku:
public class ShortCircuitingResourceFilterAttribute : Attribute, IResourceFilter
{
public void OnResourceExecuting(ResourceExecutingContext context)
{
context.Result = new ContentResult
{
Content = nameof(ShortCircuitingResourceFilterAttribute)
};
}
public void OnResourceExecuted(ResourceExecutedContext context) { }
}
W poniższym kodzie, zarówno filtr, jak [ShortCircuitingResourceFilter]
i docelowy [ResponseHeader]
Index
filtr metody akcji. Filtr ShortCircuitingResourceFilterAttribute
:
- Uruchamia najpierw, ponieważ jest to filtr zasobów i
ResponseHeaderAttribute
jest filtrem akcji. - Zwarcie pozostałej części potoku.
W związku z tym ResponseHeaderAttribute
filtr nigdy nie jest uruchamiany dla Index
akcji. To zachowanie byłoby takie samo, jeśli oba filtry zostały zastosowane na poziomie metody akcji, pod warunkiem, że uruchomiono ShortCircuitingResourceFilterAttribute
najpierw. Przebiegi ShortCircuitingResourceFilterAttribute
najpierw ze względu na jego typ filtru:
[ResponseHeader("Filter-Header", "Filter Value")]
public class ShortCircuitingController : Controller
{
[ShortCircuitingResourceFilter]
public IActionResult Index() =>
Content($"- {nameof(ShortCircuitingController)}.{nameof(Index)}");
}
Wstrzykiwanie zależności
Filtry można dodawać według typu lub wystąpienia. Jeśli wystąpienie zostanie dodane, to wystąpienie jest używane dla każdego żądania. Jeśli typ zostanie dodany, zostanie aktywowany typ. Filtr aktywowany przez typ oznacza:
- Wystąpienie jest tworzone dla każdego żądania.
- Wszystkie zależności konstruktora są wypełniane przez wstrzykiwanie zależności (DI).
Filtry implementowane jako atrybuty i dodawane bezpośrednio do klas kontrolerów lub metod akcji nie mogą mieć zależności konstruktora zapewniane przez wstrzykiwanie zależności (DI). Zależności konstruktora nie mogą być udostępniane przez di, ponieważ atrybuty muszą mieć parametry konstruktora podane tam, gdzie są stosowane.
Następujące filtry obsługują zależności konstruktora dostarczone z di:
- ServiceFilterAttribute
- TypeFilterAttribute
- IFilterFactory zaimplementowany w atrybucie .
Powyższe filtry można zastosować do kontrolera lub akcji.
Rejestratory są dostępne w witrynie DI. Należy jednak unikać tworzenia i używania filtrów wyłącznie do celów rejestrowania. Wbudowane rejestrowanie struktury zwykle zapewnia, co jest potrzebne do rejestrowania. Rejestrowanie dodane do filtrów:
- Należy skoncentrować się na problemach dotyczących domeny biznesowej lub zachowaniu specyficznym dla filtru.
- Nie należy rejestrować akcji ani innych zdarzeń struktury. Wbudowane filtry rejestrują już akcje i zdarzenia struktury.
ServiceFilterAttribute
Typy implementacji filtru usługi są rejestrowane w programie Program.cs
. Element ServiceFilterAttribute pobiera wystąpienie filtru z di.
Poniższy kod przedstawia klasę LoggingResponseHeaderFilterService
, która używa di:
public class LoggingResponseHeaderFilterService : IResultFilter
{
private readonly ILogger _logger;
public LoggingResponseHeaderFilterService(
ILogger<LoggingResponseHeaderFilterService> logger) =>
_logger = logger;
public void OnResultExecuting(ResultExecutingContext context)
{
_logger.LogInformation(
$"- {nameof(LoggingResponseHeaderFilterService)}.{nameof(OnResultExecuting)}");
context.HttpContext.Response.Headers.Add(
nameof(OnResultExecuting), nameof(LoggingResponseHeaderFilterService));
}
public void OnResultExecuted(ResultExecutedContext context)
{
_logger.LogInformation(
$"- {nameof(LoggingResponseHeaderFilterService)}.{nameof(OnResultExecuted)}");
}
}
W poniższym kodzie LoggingResponseHeaderFilterService
zostanie dodany do kontenera DI:
builder.Services.AddScoped<LoggingResponseHeaderFilterService>();
W poniższym kodzie ServiceFilter
atrybut pobiera wystąpienie filtru LoggingResponseHeaderFilterService
z di:
[ServiceFilter(typeof(LoggingResponseHeaderFilterService))]
public IActionResult WithServiceFilter() =>
Content($"- {nameof(FilterDependenciesController)}.{nameof(WithServiceFilter)}");
W przypadku używania polecenia ServiceFilterAttribute
, ustawienie ServiceFilterAttribute.IsReusable:
- Zawiera wskazówkę, że wystąpienie filtru może być ponownie używane poza zakresem żądania, w ramach którego został utworzony. Środowisko uruchomieniowe ASP.NET Core nie gwarantuje:
- Zostanie utworzone pojedyncze wystąpienie filtru.
- Filtr nie zostanie ponownie zażądany z kontenera DI w późniejszym czasie.
- Nie należy używać z filtrem, który zależy od usług z okresem istnienia innym niż pojedynczy.
ServiceFilterAttribute implementuje IFilterFactory. IFilterFactory
Uwidacznia metodę CreateInstance IFilterMetadata tworzenia wystąpienia. CreateInstance
ładuje określony typ z DI.
TypeFilterAttribute
TypeFilterAttribute jest podobny do ServiceFilterAttribute, ale jego typ nie jest rozpoznawany bezpośrednio z kontenera DI. Tworzy wystąpienie typu przy użyciu polecenia Microsoft.Extensions.DependencyInjection.ObjectFactory.
Ponieważ TypeFilterAttribute
typy nie są rozpoznawane bezpośrednio z kontenera DI:
- Typy, do których odwołuje się odwołanie przy użyciu
TypeFilterAttribute
kontenera di, nie muszą być zarejestrowane. Mają one swoje zależności spełnione przez kontener DI. TypeFilterAttribute
opcjonalnie może akceptować argumenty konstruktora dla typu.
W przypadku używania polecenia TypeFilterAttribute
, ustawienie TypeFilterAttribute.IsReusable:
Zawiera wskazówkę, że wystąpienie filtru może być ponownie używane poza zakresem żądania, w ramach którego został utworzony. Środowisko uruchomieniowe ASP.NET Core nie gwarantuje utworzenia pojedynczego wystąpienia filtru.
Nie należy używać z filtrem, który zależy od usług z okresem istnienia innym niż pojedynczy.
W poniższym przykładzie pokazano, jak przekazać argumenty do typu przy użyciu polecenia TypeFilterAttribute
:
[TypeFilter(typeof(LoggingResponseHeaderFilter),
Arguments = new object[] { "Filter-Header", "Filter Value" })]
public IActionResult WithTypeFilter() =>
Content($"- {nameof(FilterDependenciesController)}.{nameof(WithTypeFilter)}");
Filtry autoryzacji
Filtry autoryzacji:
- Czy pierwsze filtry są uruchamiane w potoku filtru.
- Kontrolowanie dostępu do metod akcji.
- Przed metodą, ale nie po metodzie.
Niestandardowe filtry autoryzacji wymagają niestandardowej struktury autoryzacji. Preferuj konfigurowanie zasad autoryzacji lub pisanie niestandardowych zasad autoryzacji w celu zapisania filtru niestandardowego. Wbudowany filtr autoryzacji:
- Wywołuje system autoryzacji.
- Nie autoryzuje żądań.
Nie zgłaszaj wyjątków w filtrach autoryzacji:
- Wyjątek nie zostanie obsłużony.
- Filtry wyjątków nie będą obsługiwać wyjątku.
Rozważ wystawienie wyzwania w przypadku wystąpienia wyjątku w filtrze autoryzacji.
Dowiedz się więcej o autoryzacji.
Filtry zasobów
Filtry zasobów:
- Zaimplementuj IResourceFilter interfejs lub IAsyncResourceFilter .
- Wykonanie opakowuje większość potoku filtru.
- Filtry autoryzacji są uruchamiane przed filtrami zasobów.
Filtry zasobów są przydatne w przypadku zwarć większość potoku. Na przykład filtr buforowania może uniknąć reszty potoku po trafieniu pamięci podręcznej.
Przykłady filtrów zasobów:
Wcześniej pokazano filtr zasobów zwarciowych.
DisableFormValueModelBindingAttribute:
- Zapobiega uzyskiwaniu dostępu do danych formularza przez powiązanie modelu.
- Służy do przekazywania dużych plików, aby zapobiec odczytywaniu danych formularza do pamięci.
Filtry akcji
Filtry akcji nie mają zastosowania do Razor stron. Razor Obsługa stron i IPageFilter IAsyncPageFilter. Aby uzyskać więcej informacji, zobacz Metody filtrowania dla Razor stron.
Filtry akcji:
- Zaimplementuj IActionFilter interfejs lub IAsyncActionFilter .
- Ich wykonywanie otacza wykonywanie metod akcji.
Poniższy kod przedstawia przykładowy filtr akcji:
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.
}
}
Element ActionExecutingContext zawiera następujące właściwości:
- ActionArguments — umożliwia odczytywanie danych wejściowych do metody akcji.
- Controller — umożliwia manipulowanie wystąpieniem kontrolera.
- Result - ustawianie
Result
zwarć wykonywania metody akcji i kolejnych filtrów akcji.
Zgłaszanie wyjątku w metodzie akcji:
- Zapobiega uruchamianiu kolejnych filtrów.
- W przeciwieństwie do ustawienia
Result
, jest traktowany jako błąd zamiast pomyślnego wyniku.
Controller
Zapewnia ActionExecutedContext i Result
oraz następujące właściwości:
- Canceled — prawda, jeśli wykonanie akcji zostało zwarcie przez inny filtr.
- Exception — Wartość inna niż null, jeśli akcja lub wcześniej uruchomiony filtr akcji zwrócił wyjątek. Ustawienie tej właściwości na null:
- Skutecznie obsługuje wyjątek.
Result
jest wykonywany tak, jakby został zwrócony z metody akcji.
W przypadku metody IAsyncActionFilter
wywołanie metody ActionExecutionDelegate:
- Wykonuje wszystkie kolejne filtry akcji i metodę akcji.
- Zwraca wartość
ActionExecutedContext
.
Aby zwarć, przypisz Microsoft.AspNetCore.Mvc.Filters.ActionExecutingContext.Result do wystąpienia wyników i nie wywołuje next
metody ().ActionExecutionDelegate
Struktura zapewnia abstrakcję ActionFilterAttribute , która może być podklasowana.
Filtr OnActionExecuting
akcji może służyć do:
- Weryfikowanie stanu modelu.
- Zwróć błąd, jeśli stan jest nieprawidłowy.
public class ValidateModelAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
if (!context.ModelState.IsValid)
{
context.Result = new BadRequestObjectResult(context.ModelState);
}
}
}
Uwaga
Kontrolery z adnotacjami z atrybutem [ApiController]
automatycznie weryfikują stan modelu i zwracają odpowiedź 400. Aby uzyskać więcej informacji, zobacz Automatyczne odpowiedzi HTTP 400.
Metoda OnActionExecuted
jest uruchamiana po metodzie akcji:
- I może wyświetlać i manipulować wynikami akcji za pośrednictwem Result właściwości .
- Canceled parametr ma wartość true, jeśli wykonanie akcji zostało zwarcie przez inny filtr.
- Exception parametr jest ustawiony na wartość inną niż null, jeśli akcja lub kolejny filtr akcji zwrócił wyjątek. Ustawienie
Exception
wartości null:- Skutecznie obsługuje wyjątek.
ActionExecutedContext.Result
jest wykonywany tak, jakby został zwrócony normalnie z metody akcji.
Filtry wyjątków
Filtry wyjątków:
- Zaimplementuj IExceptionFilter lub IAsyncExceptionFilter.
- Może służyć do implementowania typowych zasad obsługi błędów.
Poniższy przykładowy filtr wyjątku wyświetla szczegółowe informacje o wyjątkach występujących podczas opracowywania aplikacji:
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()
};
}
}
Poniższy kod testuje filtr wyjątku:
[TypeFilter(typeof(SampleExceptionFilter))]
public class ExceptionController : Controller
{
public IActionResult Index() =>
Content($"- {nameof(ExceptionController)}.{nameof(Index)}");
}
Filtry wyjątków:
- Nie mają wcześniej i po zdarzeniach.
- Zaimplementuj OnException lub OnExceptionAsync.
- Obsługa nieobsługiwanych wyjątków występujących w Razor procesie tworzenia strony lub kontrolera, powiązania modelu, filtrów akcji lub metod akcji.
- Nie przechwytuj wyjątków występujących w filtrach zasobów, filtrach wyników lub wykonywaniu wyników MVC.
Aby obsłużyć wyjątek, ustaw ExceptionHandled właściwość na true
lub przypisz Result właściwość . Spowoduje to zatrzymanie propagacji wyjątku. Filtr wyjątku nie może przekształcić wyjątku w "powodzenie". Tylko filtr akcji może to zrobić.
Filtry wyjątków:
- Są dobre w przypadku podlewek wyjątków występujących w ramach akcji.
- Nie są tak elastyczne, jak oprogramowanie pośredniczące obsługujące błędy.
Preferuj oprogramowanie pośredniczące do obsługi wyjątków. Używaj filtrów wyjątków tylko wtedy, gdy obsługa błędów różni się w zależności od wywoływanej metody akcji. Na przykład aplikacja może mieć metody akcji zarówno dla punktów końcowych interfejsu API, jak i dla widoków/HTML. Punkty końcowe interfejsu API mogą zwracać informacje o błędach jako JSWŁĄCZONE, podczas gdy akcje oparte na widoku mogą zwracać stronę błędu jako kod HTML.
Filtry wyników
Filtry wyników:
- Implementowanie interfejsu:
- Ich wykonanie otacza wykonywanie wyników akcji.
IResultFilter i IAsyncResultFilter
Poniższy kod przedstawia przykładowy filtr wyników:
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.
}
}
Rodzaj wykonywanego wyniku zależy od akcji. Akcja zwracająca widok obejmuje wszystkie przetwarzanie razor w ramach wykonywanego ViewResult elementu. Metoda interfejsu API może wykonać serializacji w ramach wykonywania wyniku. Dowiedz się więcej o wynikach akcji.
Filtry wyników są wykonywane tylko wtedy, gdy akcja lub filtr akcji generuje wynik akcji. Filtry wyników nie są wykonywane, gdy:
- Filtr autoryzacji lub filtr zasobów zwarcie potoku.
- Filtr wyjątku obsługuje wyjątek, generując wynik akcji.
Metoda Microsoft.AspNetCore.Mvc.Filters.IResultFilter.OnResultExecuting może zwarć wykonywanie wyniku akcji i kolejnych filtrów wyników, ustawiając wartość Microsoft.AspNetCore.Mvc.Filters.ResultExecutingContext.Cancel true
. Zapisz w obiekcie odpowiedzi podczas zwarć, aby uniknąć generowania pustej odpowiedzi. Zgłaszanie wyjątku w pliku IResultFilter.OnResultExecuting
:
- Zapobiega wykonywaniu wyniku akcji i kolejnych filtrów.
- Jest traktowany jako błąd zamiast pomyślnego wyniku.
Po uruchomieniu Microsoft.AspNetCore.Mvc.Filters.IResultFilter.OnResultExecuted metody odpowiedź prawdopodobnie została już wysłana do klienta. Jeśli odpowiedź została już wysłana do klienta, nie można jej zmienić.
ResultExecutedContext.Canceled
parametr jest ustawiony na true
wartość , jeśli wykonanie wyniku akcji zostało zwarcie przez inny filtr.
ResultExecutedContext.Exception
parametr jest ustawiony na wartość inną niż null, jeśli wynik akcji lub kolejny filtr wyników zwrócił wyjątek. Ustawienie Exception
wartości null skutecznie obsługuje wyjątek i uniemożliwia ponowne zgłoszenie wyjątku w dalszej części potoku. Nie ma niezawodnego sposobu zapisywania danych w odpowiedzi podczas obsługi wyjątku w filtrze wyników. Jeśli nagłówki zostały opróżnione do klienta, gdy wynik akcji zgłasza wyjątek, nie ma niezawodnego mechanizmu wysyłania kodu błędu.
W przypadku elementu IAsyncResultFilterwywołanie metody await next
na obiekcie ResultExecutionDelegate wykonuje wszystkie kolejne filtry wyników i wynik akcji. Aby zwarć, ustaw wartość ResultExecutingContext.Cancel true
i nie wywołaj metody ResultExecutionDelegate
:
public class SampleAsyncResultFilter : IAsyncResultFilter
{
public async Task OnResultExecutionAsync(
ResultExecutingContext context, ResultExecutionDelegate next)
{
if (context.Result is not EmptyResult)
{
await next();
}
else
{
context.Cancel = true;
}
}
}
Struktura zapewnia abstrakcję ResultFilterAttribute
, która może być podklasowana. Pokazana wcześniej klasa ResponseHeaderAttribute jest przykładem atrybutu filtru wyników.
IAlwaysRunResultFilter i IAsyncAlwaysRunResultFilter
Interfejsy IAlwaysRunResultFilter i IAsyncAlwaysRunResultFilter deklarują implementację uruchamianą IResultFilter dla wszystkich wyników akcji. Obejmuje to wyniki akcji wygenerowane przez:
- Filtry autoryzacji i filtry zasobów, które są zwarcie.
- Filtry wyjątków.
Na przykład następujący filtr zawsze jest uruchamiany i ustawia wynik akcji (ObjectResult) z kodem stanu jednostki nieprzetworzonej 422, gdy negocjowanie zawartości kończy się niepowodzeniem:
public class UnprocessableResultFilter : IAlwaysRunResultFilter
{
public void OnResultExecuting(ResultExecutingContext context)
{
if (context.Result is StatusCodeResult statusCodeResult
&& statusCodeResult.StatusCode == StatusCodes.Status415UnsupportedMediaType)
{
context.Result = new ObjectResult("Unprocessable")
{
StatusCode = StatusCodes.Status422UnprocessableEntity
};
}
}
public void OnResultExecuted(ResultExecutedContext context) { }
}
IFilterFactory
IFilterFactory implementuje IFilterMetadata. IFilterFactory
W związku z tym wystąpienie może być używane jako IFilterMetadata
wystąpienie w dowolnym miejscu w potoku filtru. Gdy środowisko uruchomieniowe przygotowuje się do wywołania filtru, próbuje rzutować go do elementu IFilterFactory
. Jeśli rzutowanie powiedzie się, metoda jest wywoływana w CreateInstance celu utworzenia wywoływanego IFilterMetadata
wystąpienia. Zapewnia to elastyczny projekt, ponieważ dokładny potok filtru nie musi być jawnie ustawiany podczas uruchamiania aplikacji.
IFilterFactory.IsReusable
:
- Jest wskazówką fabryki, że wystąpienie filtru utworzone przez fabrykę może być ponownie używane poza zakresem żądania, w ramach którego został utworzony.
- Nie należy używać z filtrem, który zależy od usług z okresem istnienia innym niż pojedynczy.
Środowisko uruchomieniowe ASP.NET Core nie gwarantuje:
- Zostanie utworzone pojedyncze wystąpienie filtru.
- Filtr nie zostanie ponownie zażądany z kontenera DI w późniejszym czasie.
Ostrzeżenie
Skonfiguruj wartość tak, aby zwracać IFilterFactory.IsReusable true
tylko wtedy, gdy źródło filtrów jest jednoznaczne, filtry są bezstanowe, a filtry są bezpieczne do użycia w wielu żądaniach HTTP. Na przykład nie zwracaj filtrów z di, które są zarejestrowane jako zakres lub przejściowe, jeśli IFilterFactory.IsReusable
zwraca wartość true
.
IFilterFactory
można zaimplementować przy użyciu implementacji atrybutów niestandardowych jako innego podejścia do tworzenia filtrów:
public class ResponseHeaderFilterFactory : Attribute, IFilterFactory
{
public bool IsReusable => false;
public IFilterMetadata CreateInstance(IServiceProvider serviceProvider) =>
new InternalResponseHeaderFilter();
private class InternalResponseHeaderFilter : IActionFilter
{
public void OnActionExecuting(ActionExecutingContext context) =>
context.HttpContext.Response.Headers.Add(
nameof(OnActionExecuting), nameof(InternalResponseHeaderFilter));
public void OnActionExecuted(ActionExecutedContext context) { }
}
Filtr jest stosowany w następującym kodzie:
[ResponseHeaderFilterFactory]
public IActionResult Index() =>
Content($"- {nameof(FilterFactoryController)}.{nameof(Index)}");
IFilterFactory zaimplementowany w atrybucie
Filtry implementujące IFilterFactory
są przydatne w przypadku filtrów, które:
- Nie wymagaj przekazywania parametrów.
- Mieć zależności konstruktora, które muszą być wypełnione przez di.
TypeFilterAttribute implementuje IFilterFactory. IFilterFactory
Uwidacznia metodę CreateInstance IFilterMetadata tworzenia wystąpienia. CreateInstance
ładuje określony typ z kontenera usług (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)}");
}
}
}
Poniższy kod przedstawia trzy podejścia do zastosowania filtru:
[SampleActionTypeFilter]
public IActionResult WithDirectAttribute() =>
Content($"- {nameof(FilterFactoryController)}.{nameof(WithDirectAttribute)}");
[TypeFilter(typeof(SampleActionTypeFilterAttribute))]
public IActionResult WithTypeFilterAttribute() =>
Content($"- {nameof(FilterFactoryController)}.{nameof(WithTypeFilterAttribute)}");
[ServiceFilter(typeof(SampleActionTypeFilterAttribute))]
public IActionResult WithServiceFilterAttribute() =>
Content($"- {nameof(FilterFactoryController)}.{nameof(WithServiceFilterAttribute)}");
W poprzednim kodzie preferowane jest pierwsze podejście do zastosowania filtru.
Używanie oprogramowania pośredniczącego w potoku filtru
Filtry zasobów działają jak oprogramowanie pośredniczące, które otaczają wykonywanie wszystkich elementów, które są dostępne później w potoku. Jednak filtry różnią się od oprogramowania pośredniczącego, ponieważ są one częścią środowiska uruchomieniowego, co oznacza, że mają dostęp do kontekstu i konstrukcji.
Aby użyć oprogramowania pośredniczącego jako filtru, utwórz typ z metodą określającą Configure
oprogramowanie pośredniczące do wstrzykiwania do potoku filtru. W poniższym przykładzie użyto oprogramowania pośredniczącego do ustawienia nagłówka odpowiedzi:
public class FilterMiddlewarePipeline
{
public void Configure(IApplicationBuilder app)
{
app.Use(async (context, next) =>
{
context.Response.Headers.Add("Pipeline", "Middleware");
await next();
});
}
}
Użyj polecenia , MiddlewareFilterAttribute aby uruchomić oprogramowanie pośredniczące:
[MiddlewareFilter(typeof(FilterMiddlewarePipeline))]
public class FilterMiddlewareController : Controller
{
public IActionResult Index() =>
Content($"- {nameof(FilterMiddlewareController)}.{nameof(Index)}");
}
Filtry oprogramowania pośredniczącego są uruchamiane na tym samym etapie potoku filtru co filtry zasobów, przed powiązaniem modelu i po pozostałej części potoku.
Bezpieczeństwo wątkowe
Podczas przekazywania wystąpienia filtru do Add
elementu , a nie do Type
elementu , filtr jest pojedynczym elementem i nie jest bezpieczny wątkowo.
Dodatkowe zasoby
Przez Kirk Larkin, Rick Anderson, Tom Dykstra i Steve Smith
Filtry w ASP.NET Core umożliwiają uruchamianie kodu przed lub po określonych etapach w potoku przetwarzania żądań.
Wbudowane filtry obsługują zadania, takie jak:
- Autoryzacja, uniemożliwiająca dostęp do zasobów, dla których użytkownik nie jest autoryzowany.
- Buforowanie odpowiedzi, zwarcie potoku żądania w celu zwrócenia buforowanej odpowiedzi.
Filtry niestandardowe można tworzyć w celu obsługi problemów związanych z wycinaniami krzyżowymi. Przykłady problemów obejmujących krzyżowe obejmują obsługę błędów, buforowanie, konfigurację, autoryzację i rejestrowanie. Filtry unikają duplikowania kodu. Na przykład filtr wyjątków obsługi błędów może skonsolidować obsługę błędów.
Ten dokument dotyczy Razor stron, kontrolerów interfejsu API i kontrolerów z widokami. Filtry nie działają bezpośrednio ze składnikamiRazor. Filtr może mieć wpływ tylko pośrednio na składnik, gdy:
- Składnik jest osadzony na stronie lub w widoku.
- Strona lub kontroler i widok używają filtru.
Wyświetl lub pobierz przykład (jak pobrać).
Jak działają filtry
Filtry są uruchamiane w potoku wywołania akcji ASP.NET Core, czasami nazywane potokiem filtru. Potok filtru jest uruchamiany po wybraniu akcji do wykonania przez ASP.NET Core.
Typy filtrów
Każdy typ filtru jest wykonywany na innym etapie w potoku filtru:
Filtry autoryzacji są uruchamiane jako pierwsze i służą do określania, czy użytkownik jest autoryzowany do żądania. Autoryzacja filtruje zwarcie potoku, jeśli żądanie nie jest autoryzowane.
Filtry zasobów:
- Uruchom polecenie po autoryzacji.
- OnResourceExecuting uruchamia kod przed resztą potoku filtru. Na przykład
OnResourceExecuting
uruchamia kod przed powiązaniem modelu. - OnResourceExecuted uruchamia kod po zakończeniu pozostałej części potoku.
Filtry akcji:
- Uruchom kod bezpośrednio przed wywołaną metodą akcji i po jej wywołaniu.
- Może zmienić argumenty przekazane do akcji.
- Może zmienić wynik zwrócony z akcji.
- Nie są obsługiwane na Razor stronach.
Filtry wyjątków stosują zasady globalne do nieobsługiwanych wyjątków występujących przed zapisaniem treści odpowiedzi.
Filtry wyników uruchamiają kod bezpośrednio przed i po wykonaniu wyników akcji. Są one uruchamiane tylko wtedy, gdy metoda akcji została wykonana pomyślnie. Są one przydatne w przypadku logiki, która musi otaczać wykonywanie widoku lub formatera.
Na poniższym diagramie przedstawiono sposób interakcji typów filtrów w potoku filtru.
Implementacja
Filtry obsługują zarówno implementacje synchroniczne, jak i asynchroniczne za pomocą różnych definicji interfejsu.
Filtry synchroniczne uruchamiają kod przed i po etapie potoku. Na przykład OnActionExecuting jest wywoływana przed wywołaniem metody akcji. OnActionExecuted jest wywoływana po zwracaniu metody akcji.
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);
}
}
W poprzednim kodzie MyDebug jest funkcją narzędzia w przykładowym pobieraniu.
Filtry asynchroniczne definiują metodę On-Stage-ExecutionAsync
. Na przykład : 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.
}
}
W poprzednim kodzie SampleAsyncActionFilter
element ma ActionExecutionDelegate wartość (next
), która wykonuje metodę akcji.
Wiele etapów filtrowania
Interfejsy dla wielu etapów filtrowania można zaimplementować w jednej klasie. Na przykład ActionFilterAttribute klasa implementuje:
- Synchroniczne: IActionFilter i IResultFilter
- Asynchroniczne: IAsyncActionFilter i IAsyncResultFilter
- IOrderedFilter
Zaimplementuj synchroniczną lub asynchroniczną wersję interfejsu filtru, a nie obie. Środowisko uruchomieniowe najpierw sprawdza, czy filtr implementuje interfejs asynchroniczny, a jeśli tak, wywołuje to. Jeśli nie, wywołuje metody interfejsu synchronicznego. Jeśli oba interfejsy asynchroniczne i synchroniczne są implementowane w jednej klasie, wywoływana jest tylko metoda asynchroniczna. W przypadku używania klas abstrakcyjnych, takich jak ActionFilterAttribute, przesłaniaj tylko metody synchroniczne lub metody asynchroniczne dla każdego typu filtru.
Wbudowane atrybuty filtru
ASP.NET Core zawiera wbudowane filtry oparte na atrybutach, które mogą być podklasowane i dostosowane. Na przykład następujący filtr wyników dodaje nagłówek do odpowiedzi:
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);
}
}
Atrybuty umożliwiają filtrowanie akceptowania argumentów, jak pokazano w poprzednim przykładzie. Zastosuj element AddHeaderAttribute
do kontrolera lub metody akcji i określ nazwę i wartość nagłówka HTTP:
[AddHeader("Author", "Rick Anderson")]
public class SampleController : Controller
{
public IActionResult Index()
{
return Content("Examine the headers using the F12 developer tools.");
}
Użyj narzędzia, takiego jak narzędzia deweloperskie przeglądarki, aby zbadać nagłówki. W obszarze Nagłówkiauthor: Rick Anderson
odpowiedzi zostanie wyświetlona wartość .
Poniższy kod implementuje ten ActionFilterAttribute
kod:
- Odczytuje tytuł i nazwę z systemu konfiguracji. W przeciwieństwie do poprzedniego przykładu poniższy kod nie wymaga dodania parametrów filtru do kodu.
- Dodaje tytuł i nazwę do nagłówka odpowiedzi.
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);
}
}
Opcje konfiguracji są udostępniane z systemu konfiguracji przy użyciu wzorca opcji. Na przykład z appsettings.json
pliku:
{
"Position": {
"Title": "Editor",
"Name": "Joe Smith"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*"
}
W pliku StartUp.ConfigureServices
:
- Klasa
PositionOptions
jest dodawana do kontenera usługi z obszarem"Position"
konfiguracji. - Element
MyActionFilterAttribute
jest dodawany do kontenera usługi.
public void ConfigureServices(IServiceCollection services)
{
services.Configure<PositionOptions>(
Configuration.GetSection("Position"));
services.AddScoped<MyActionFilterAttribute>();
services.AddControllersWithViews();
}
Poniższy kod przedstawia klasę PositionOptions
:
public class PositionOptions
{
public string Title { get; set; }
public string Name { get; set; }
}
Poniższy kod ma zastosowanie MyActionFilterAttribute
do Index2
metody :
[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.");
}
W obszarze Nagłówkiauthor: Rick Anderson
odpowiedzi , i Editor: Joe Smith
jest wyświetlany po wywołaniu punktu końcowegoSample/Index2
.
Poniższy kod stosuje element MyActionFilterAttribute
i AddHeaderAttribute
do Razor strony:
[AddHeader("Author", "Rick Anderson")]
[ServiceFilter(typeof(MyActionFilterAttribute))]
public class IndexModel : PageModel
{
public void OnGet()
{
}
}
Nie można zastosować filtrów do Razor metod obsługi strony. Można je zastosować do Razor modelu strony lub globalnie.
Kilka interfejsów filtru ma odpowiednie atrybuty, których można używać jako klas bazowych na potrzeby implementacji niestandardowych.
Atrybuty filtru:
- ActionFilterAttribute
- ExceptionFilterAttribute
- ResultFilterAttribute
- FormatFilterAttribute
- ServiceFilterAttribute
- TypeFilterAttribute
Filtrowanie zakresów i kolejności wykonywania
Filtr można dodać do potoku w jednym z trzech zakresów:
- Używanie atrybutu w akcji kontrolera. Nie można zastosować atrybutów filtru do Razor metod obsługi stron.
- Używanie atrybutu na kontrolerze lub Razor stronie.
- Globalnie dla wszystkich kontrolerów, akcji i Razor stron, jak pokazano w poniższym kodzie:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews(options =>
{
options.Filters.Add(typeof(MySampleActionFilter));
});
}
Domyślna kolejność wykonywania
Jeśli istnieje wiele filtrów dla określonego etapu potoku, zakres określa domyślną kolejność wykonywania filtru. Filtry globalne otaczają filtry klas, które z kolei otaczają filtry metod.
W wyniku zagnieżdżania filtrów kod po filtrach jest uruchamiany w odwrotnej kolejności przed kodem. Sekwencja filtru:
- Przed kodem filtrów globalnych.
- Przed kodem filtrów kontrolera i Razor strony.
- Przed kodem metody akcji filtruje.
- Po kodzie metody akcji filtruje.
- Po kodzie kontrolera i Razor filtrów strony.
- Przed kodem filtrów kontrolera i Razor strony.
- Po kodzie filtrów globalnych.
Poniższy przykład ilustrujący kolejność wywoływanych metod filtrowania dla filtrów akcji synchronicznych.
Sequence | Zakres filtru | Metoda filter |
---|---|---|
1 | Globalnie | OnActionExecuting |
2 | Kontroler lub Razor strona | OnActionExecuting |
3 | Method | OnActionExecuting |
100 | Method | OnActionExecuted |
5 | Kontroler lub Razor strona | OnActionExecuted |
6 | Globalnie | OnActionExecuted |
Filtry na poziomie kontrolera
Każdy kontroler dziedziczony z klasy bazowej Controller obejmuje Controller.OnActionExecutingmetody , Controller.OnActionExecutionAsynci Controller.OnActionExecutedOnActionExecuted
. Te metody:
- Zawijaj filtry uruchamiane dla danej akcji.
OnActionExecuting
jest wywoływana przed dowolnym filtrem akcji.OnActionExecuted
jest wywoływany po wszystkich filtrach akcji.OnActionExecutionAsync
jest wywoływana przed dowolnym filtrem akcji. Kod w filtrze po uruchomieniu ponext
uruchomieniu po metodzie akcji.
Na przykład w przykładzie MySampleActionFilter
pobierania aplikacja jest stosowana globalnie podczas uruchamiania.
Pomocnik TestController
:
- Stosuje element
SampleActionFilterAttribute
([SampleActionFilter]
) doFilterTest2
akcji. - Przesłonięcia
OnActionExecuting
iOnActionExecuted
.
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 jest dostarczany przez pakiet NuGet Rick.Docs.Samples.RouteInfo i wyświetla informacje o trasie.
Przechodzenie do https://localhost:5001/Test/FilterTest2
uruchomi następującego kodu:
TestController.OnActionExecuting
MySampleActionFilter.OnActionExecuting
SampleActionFilterAttribute.OnActionExecuting
TestController.FilterTest2
SampleActionFilterAttribute.OnActionExecuted
MySampleActionFilter.OnActionExecuted
TestController.OnActionExecuted
Filtry na poziomie kontrolera ustawiają właściwość Order na int.MinValue
. Filtry na poziomie kontrolera nie można ustawić tak, aby były uruchamiane po zastosowaniu filtrów do metod. Kolejność jest wyjaśniona w następnej sekcji.
Aby uzyskać informacje na temat Razor stron, zobacz Implementowanie Razor filtrów stron przez zastępowanie metod filtrowania.
Zastępowanie kolejności domyślnej
Domyślną sekwencję wykonywania można zastąpić przez zaimplementowanie metody IOrderedFilter. IOrderedFilter
Uwidacznia Order właściwość, która ma pierwszeństwo przed zakresem, aby określić kolejność wykonywania. Filtr o niższej Order
wartości:
- Uruchamia przed kodem przed filtrem o wyższej wartości
Order
. - Uruchamia po kodzie po filtrze z wyższą
Order
wartością.
Właściwość Order
jest ustawiana za pomocą parametru konstruktora:
[SampleActionFilter(Order = int.MinValue)]
Rozważ dwa filtry akcji w następującym kontrolerze:
[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);
}
}
Filtr globalny jest dodawany w pliku StartUp.ConfigureServices
:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews(options =>
{
options.Filters.Add(typeof(MySampleActionFilter));
});
}
3 filtry są uruchamiane w następującej kolejności:
Test2Controller.OnActionExecuting
MySampleActionFilter.OnActionExecuting
MyAction2FilterAttribute.OnActionExecuting
Test2Controller.FilterTest2
MyAction2FilterAttribute.OnResultExecuting
MySampleActionFilter.OnActionExecuted
Test2Controller.OnActionExecuted
Właściwość Order
zastępuje zakres podczas określania kolejności uruchamiania filtrów. Filtry są sortowane najpierw według kolejności, a następnie zakres jest używany do przerywania więzi. Wszystkie wbudowane filtry implementują IOrderedFilter
i ustawiają wartość domyślną Order
na 0. Jak wspomniano wcześniej, filtry na poziomie kontrolera ustawiają właściwość Order na int.MinValue
Wartość Dla wbudowanych filtrów, zakres określa kolejność, chyba że Order
ustawiono wartość inną niż zero.
W poprzednim kodzie ma zakres globalny, MySampleActionFilter
dlatego jest uruchamiany przed elementem MyAction2FilterAttribute
, który ma zakres kontrolera. Aby najpierw wykonać MyAction2FilterAttribute
uruchomienie, ustaw kolejność na int.MinValue
:
[MyAction2Filter(int.MinValue)]
public class Test2Controller : Controller
{
public IActionResult FilterTest2()
{
return ControllerContext.MyDisplayRouteInfo();
}
public override void OnActionExecuting(ActionExecutingContext context)
{
// Do something before the action executes.
MyDebug.Write(MethodBase.GetCurrentMethod(), HttpContext.Request.Path);
base.OnActionExecuting(context);
}
public override void OnActionExecuted(ActionExecutedContext context)
{
// Do something after the action executes.
MyDebug.Write(MethodBase.GetCurrentMethod(), HttpContext.Request.Path);
base.OnActionExecuted(context);
}
}
Aby najpierw uruchomić filtr MySampleActionFilter
globalny, ustaw wartość Order
int.MinValue
:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews(options =>
{
options.Filters.Add(typeof(MySampleActionFilter),
int.MinValue);
});
}
Anulowanie i zwarcie
Potok filtru można zwarcić, ustawiając Result właściwość parametru ResourceExecutingContext podanego w metodzie filtru. Na przykład następujący filtr zasobu uniemożliwia wykonywanie pozostałej części potoku:
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)
{
}
}
W poniższym kodzie, zarówno filtr, jak ShortCircuitingResourceFilter
i docelowy AddHeader
SomeResource
filtr metody akcji. Pomocnik ShortCircuitingResourceFilter
:
- Uruchamia najpierw, ponieważ jest to filtr zasobów i
AddHeader
jest filtrem akcji. - Zwarcie pozostałej części potoku.
W związku z tym AddHeader
filtr nigdy nie jest uruchamiany dla SomeResource
akcji. To zachowanie byłoby takie samo, jeśli oba filtry zostały zastosowane na poziomie metody akcji, pod warunkiem, że uruchomiono ShortCircuitingResourceFilter
najpierw. Uruchamia ShortCircuitingResourceFilter
najpierw ze względu na typ filtru lub jawne Order
użycie właściwości.
[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.");
}
}
Wstrzykiwanie zależności
Filtry można dodawać według typu lub wystąpienia. Jeśli wystąpienie zostanie dodane, to wystąpienie jest używane dla każdego żądania. Jeśli typ zostanie dodany, zostanie aktywowany typ. Filtr aktywowany przez typ oznacza:
- Wystąpienie jest tworzone dla każdego żądania.
- Wszystkie zależności konstruktora są wypełniane przez wstrzykiwanie zależności (DI).
Filtry implementowane jako atrybuty i dodawane bezpośrednio do klas kontrolerów lub metod akcji nie mogą mieć zależności konstruktora zapewniane przez wstrzykiwanie zależności (DI). Zależności konstruktora nie mogą być udostępniane przez di, ponieważ:
- Atrybuty muszą mieć parametry konstruktora podane tam, gdzie są stosowane.
- Jest to ograniczenie działania atrybutów.
Następujące filtry obsługują zależności konstruktora dostarczone z di:
- ServiceFilterAttribute
- TypeFilterAttribute
- IFilterFactory zaimplementowany w atrybucie .
Powyższe filtry można zastosować do kontrolera lub metody akcji:
Rejestratory są dostępne w witrynie DI. Należy jednak unikać tworzenia i używania filtrów wyłącznie do celów rejestrowania. Wbudowane rejestrowanie struktury zwykle zapewnia, co jest potrzebne do rejestrowania. Rejestrowanie dodane do filtrów:
- Należy skoncentrować się na problemach dotyczących domeny biznesowej lub zachowaniu specyficznym dla filtru.
- Nie należy rejestrować akcji ani innych zdarzeń struktury. Wbudowane filtry rejestrują akcje i zdarzenia struktury.
ServiceFilterAttribute
Typy implementacji filtru usługi są rejestrowane w programie ConfigureServices
. Element ServiceFilterAttribute pobiera wystąpienie filtru z di.
Poniższy kod przedstawia element 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");
}
}
W poniższym kodzie AddHeaderResultServiceFilter
zostanie dodany do kontenera DI:
public void ConfigureServices(IServiceCollection services)
{
// Add service filters.
services.AddScoped<AddHeaderResultServiceFilter>();
services.AddScoped<SampleActionFilterAttribute>();
services.AddControllersWithViews(options =>
{
options.Filters.Add(new AddHeaderAttribute("GlobalAddHeader",
"Result filter added to MvcOptions.Filters")); // An instance
options.Filters.Add(typeof(MySampleActionFilter)); // By type
options.Filters.Add(new SampleGlobalActionFilter()); // An instance
});
}
W poniższym kodzie ServiceFilter
atrybut pobiera wystąpienie filtru AddHeaderResultServiceFilter
z di:
[ServiceFilter(typeof(AddHeaderResultServiceFilter))]
public IActionResult Index()
{
return View();
}
W przypadku używania polecenia ServiceFilterAttribute
, ustawienie ServiceFilterAttribute.IsReusable:
Zawiera wskazówkę, że wystąpienie filtru może być ponownie używane poza zakresem żądania, w ramach którego został utworzony. Środowisko uruchomieniowe ASP.NET Core nie gwarantuje:
- Zostanie utworzone pojedyncze wystąpienie filtru.
- Filtr nie zostanie ponownie zażądany z kontenera DI w późniejszym czasie.
Nie należy używać z filtrem, który zależy od usług z okresem istnienia innym niż pojedynczy.
ServiceFilterAttribute implementuje IFilterFactory. IFilterFactory
Uwidacznia metodę CreateInstance IFilterMetadata tworzenia wystąpienia. CreateInstance
ładuje określony typ z DI.
TypeFilterAttribute
TypeFilterAttribute jest podobny do ServiceFilterAttribute, ale jego typ nie jest rozpoznawany bezpośrednio z kontenera DI. Tworzy wystąpienie typu przy użyciu polecenia Microsoft.Extensions.DependencyInjection.ObjectFactory.
Ponieważ TypeFilterAttribute
typy nie są rozpoznawane bezpośrednio z kontenera DI:
- Typy, do których odwołuje się odwołanie przy użyciu
TypeFilterAttribute
kontenera di, nie muszą być zarejestrowane. Mają one swoje zależności spełnione przez kontener DI. TypeFilterAttribute
opcjonalnie może akceptować argumenty konstruktora dla typu.
W przypadku używania polecenia TypeFilterAttribute
, ustawienie TypeFilterAttribute.IsReusable:
Zawiera wskazówkę, że wystąpienie filtru może być ponownie używane poza zakresem żądania, w ramach którego został utworzony. Środowisko uruchomieniowe ASP.NET Core nie gwarantuje utworzenia pojedynczego wystąpienia filtru.
Nie należy używać z filtrem, który zależy od usług z okresem istnienia innym niż pojedynczy.
W poniższym przykładzie pokazano, jak przekazać argumenty do typu przy użyciu polecenia TypeFilterAttribute
:
[TypeFilter(typeof(LogConstantFilter),
Arguments = new object[] { "Method 'Hi' called" })]
public IActionResult Hi(string name)
{
return Content($"Hi {name}");
}
Filtry autoryzacji
Filtry autoryzacji:
- Czy pierwsze filtry są uruchamiane w potoku filtru.
- Kontrolowanie dostępu do metod akcji.
- Przed metodą, ale nie po metodzie.
Niestandardowe filtry autoryzacji wymagają niestandardowej struktury autoryzacji. Preferuj konfigurowanie zasad autoryzacji lub pisanie niestandardowych zasad autoryzacji w celu zapisania filtru niestandardowego. Wbudowany filtr autoryzacji:
- Wywołuje system autoryzacji.
- Nie autoryzuje żądań.
Nie zgłaszaj wyjątków w filtrach autoryzacji:
- Wyjątek nie zostanie obsłużony.
- Filtry wyjątków nie będą obsługiwać wyjątku.
Rozważ wystawienie wyzwania w przypadku wystąpienia wyjątku w filtrze autoryzacji.
Dowiedz się więcej o autoryzacji.
Filtry zasobów
Filtry zasobów:
- Zaimplementuj IResourceFilter interfejs lub IAsyncResourceFilter .
- Wykonanie opakowuje większość potoku filtru.
- Filtry autoryzacji są uruchamiane przed filtrami zasobów.
Filtry zasobów są przydatne w przypadku zwarć większość potoku. Na przykład filtr buforowania może uniknąć reszty potoku po trafieniu pamięci podręcznej.
Przykłady filtrów zasobów:
Wcześniej pokazano filtr zasobów zwarciowych.
DisableFormValueModelBindingAttribute:
- Zapobiega uzyskiwaniu dostępu do danych formularza przez powiązanie modelu.
- Służy do przekazywania dużych plików, aby zapobiec odczytywaniu danych formularza do pamięci.
Filtry akcji
Filtry akcji nie mają zastosowania do Razor stron. Razor Obsługa stron i IPageFilter IAsyncPageFilter. Aby uzyskać więcej informacji, zobacz Metody filtrowania dla Razor stron.
Filtry akcji:
- Zaimplementuj IActionFilter interfejs lub IAsyncActionFilter .
- Ich wykonywanie otacza wykonywanie metod akcji.
Poniższy kod przedstawia przykładowy filtr akcji:
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);
}
}
Element ActionExecutingContext zawiera następujące właściwości:
- ActionArguments — umożliwia odczytywanie danych wejściowych do metody akcji.
- Controller — umożliwia manipulowanie wystąpieniem kontrolera.
- Result - ustawianie
Result
zwarć wykonywania metody akcji i kolejnych filtrów akcji.
Zgłaszanie wyjątku w metodzie akcji:
- Zapobiega uruchamianiu kolejnych filtrów.
- W przeciwieństwie do ustawienia
Result
, jest traktowany jako błąd zamiast pomyślnego wyniku.
Controller
Zapewnia ActionExecutedContext i Result
oraz następujące właściwości:
Canceled — prawda, jeśli wykonanie akcji zostało zwarcie przez inny filtr.
Exception — Wartość inna niż null, jeśli akcja lub wcześniej uruchomiony filtr akcji zwrócił wyjątek. Ustawienie tej właściwości na null:
- Skutecznie obsługuje wyjątek.
Result
jest wykonywany tak, jakby został zwrócony z metody akcji.
W przypadku metody IAsyncActionFilter
wywołanie metody ActionExecutionDelegate:
- Wykonuje wszystkie kolejne filtry akcji i metodę akcji.
- Zwraca wartość
ActionExecutedContext
.
Aby zwarć, przypisz Microsoft.AspNetCore.Mvc.Filters.ActionExecutingContext.Result do wystąpienia wyników i nie wywołuje next
metody ().ActionExecutionDelegate
Struktura zapewnia abstrakcję ActionFilterAttribute , która może być podklasowana.
Filtr OnActionExecuting
akcji może służyć do:
- Weryfikowanie stanu modelu.
- Zwróć błąd, jeśli stan jest nieprawidłowy.
public class ValidateModelAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext
context)
{
if (!context.ModelState.IsValid)
{
context.Result = new BadRequestObjectResult(
context.ModelState);
}
}
Uwaga
Kontrolery z adnotacjami z atrybutem [ApiController]
automatycznie weryfikują stan modelu i zwracają odpowiedź 400. Aby uzyskać więcej informacji, zobacz Automatyczne odpowiedzi HTTP 400.
Metoda OnActionExecuted
jest uruchamiana po metodzie akcji:
I może wyświetlać i manipulować wynikami akcji za pośrednictwem Result właściwości .
Canceled parametr ma wartość true, jeśli wykonanie akcji zostało zwarcie przez inny filtr.
Exception parametr jest ustawiony na wartość inną niż null, jeśli akcja lub kolejny filtr akcji zwrócił wyjątek. Ustawienie
Exception
wartości null:- Skutecznie obsługuje wyjątek.
ActionExecutedContext.Result
jest wykonywany tak, jakby został zwrócony normalnie z metody akcji.
public class ValidateModelAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext
context)
{
if (!context.ModelState.IsValid)
{
context.Result = new BadRequestObjectResult(
context.ModelState);
}
}
public override void OnActionExecuted(ActionExecutedContext
context)
{
var result = context.Result;
// Do something with Result.
if (context.Canceled == true)
{
// Action execution was short-circuited by another filter.
}
if(context.Exception != null)
{
// Exception thrown by action or action filter.
// Set to null to handle the exception.
context.Exception = null;
}
base.OnActionExecuted(context);
}
}
Filtry wyjątków
Filtry wyjątków:
- Zaimplementuj IExceptionFilter lub IAsyncExceptionFilter.
- Może służyć do implementowania typowych zasad obsługi błędów.
Poniższy przykładowy filtr wyjątków używa niestandardowego widoku błędu, aby wyświetlić szczegółowe informacje o wyjątkach występujących podczas opracowywania aplikacji:
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;
}
}
Poniższy kod testuje filtr wyjątku:
[TypeFilter(typeof(CustomExceptionFilter))]
public class FailingController : Controller
{
[AddHeader("Failing Controller",
"Won't appear when exception is handled")]
public IActionResult Index()
{
throw new Exception("Testing custom exception filter.");
}
}
Filtry wyjątków:
- Nie mają wcześniej i po zdarzeniach.
- Zaimplementuj OnException lub OnExceptionAsync.
- Obsługa nieobsługiwanych wyjątków występujących w Razor procesie tworzenia strony lub kontrolera, powiązania modelu, filtrów akcji lub metod akcji.
- Nie przechwytuj wyjątków występujących w filtrach zasobów, filtrach wyników lub wykonywaniu wyników MVC.
Aby obsłużyć wyjątek, ustaw ExceptionHandled właściwość na true
lub przypisz Result właściwość . Spowoduje to zatrzymanie propagacji wyjątku. Filtr wyjątku nie może przekształcić wyjątku w "powodzenie". Tylko filtr akcji może to zrobić.
Filtry wyjątków:
- Są dobre w przypadku podlewek wyjątków występujących w ramach akcji.
- Nie są tak elastyczne, jak oprogramowanie pośredniczące obsługujące błędy.
Preferuj oprogramowanie pośredniczące do obsługi wyjątków. Używaj filtrów wyjątków tylko wtedy, gdy obsługa błędów różni się w zależności od wywoływanej metody akcji. Na przykład aplikacja może mieć metody akcji zarówno dla punktów końcowych interfejsu API, jak i dla widoków/HTML. Punkty końcowe interfejsu API mogą zwracać informacje o błędach jako JSWŁĄCZONE, podczas gdy akcje oparte na widoku mogą zwracać stronę błędu jako kod HTML.
Filtry wyników
Filtry wyników:
- Implementowanie interfejsu:
- Ich wykonanie otacza wykonywanie wyników akcji.
IResultFilter i IAsyncResultFilter
Poniższy kod przedstawia filtr wyników, który dodaje nagłówek HTTP:
public class AddHeaderResultServiceFilter : IResultFilter
{
private ILogger _logger;
public AddHeaderResultServiceFilter(ILoggerFactory loggerFactory)
{
_logger = loggerFactory.CreateLogger<AddHeaderResultServiceFilter>();
}
public void OnResultExecuting(ResultExecutingContext context)
{
var headerName = "OnResultExecuting";
context.HttpContext.Response.Headers.Add(
headerName, new string[] { "ResultExecutingSuccessfully" });
_logger.LogInformation("Header added: {HeaderName}", headerName);
}
public void OnResultExecuted(ResultExecutedContext context)
{
// Can't add to headers here because response has started.
_logger.LogInformation("AddHeaderResultServiceFilter.OnResultExecuted");
}
}
Rodzaj wykonywanego wyniku zależy od akcji. Akcja zwracająca widok obejmuje wszystkie przetwarzanie razor w ramach wykonywanego ViewResult elementu. Metoda interfejsu API może wykonać serializacji w ramach wykonywania wyniku. Dowiedz się więcej o wynikach akcji.
Filtry wyników są wykonywane tylko wtedy, gdy akcja lub filtr akcji generuje wynik akcji. Filtry wyników nie są wykonywane, gdy:
- Filtr autoryzacji lub filtr zasobów zwarcie potoku.
- Filtr wyjątku obsługuje wyjątek, generując wynik akcji.
Metoda Microsoft.AspNetCore.Mvc.Filters.IResultFilter.OnResultExecuting może zwarć wykonywanie wyniku akcji i kolejnych filtrów wyników, ustawiając wartość Microsoft.AspNetCore.Mvc.Filters.ResultExecutingContext.Cancel true
. Zapisz w obiekcie odpowiedzi podczas zwarć, aby uniknąć generowania pustej odpowiedzi. Zgłaszanie wyjątku w pliku IResultFilter.OnResultExecuting
:
- Zapobiega wykonywaniu wyniku akcji i kolejnych filtrów.
- Jest traktowany jako błąd zamiast pomyślnego wyniku.
Po uruchomieniu Microsoft.AspNetCore.Mvc.Filters.IResultFilter.OnResultExecuted metody odpowiedź prawdopodobnie została już wysłana do klienta. Jeśli odpowiedź została już wysłana do klienta, nie można jej zmienić.
ResultExecutedContext.Canceled
parametr jest ustawiony na true
wartość , jeśli wykonanie wyniku akcji zostało zwarcie przez inny filtr.
ResultExecutedContext.Exception
parametr jest ustawiony na wartość inną niż null, jeśli wynik akcji lub kolejny filtr wyników zwrócił wyjątek. Ustawienie Exception
wartości null skutecznie obsługuje wyjątek i uniemożliwia ponowne zgłoszenie wyjątku w dalszej części potoku. Nie ma niezawodnego sposobu zapisywania danych w odpowiedzi podczas obsługi wyjątku w filtrze wyników. Jeśli nagłówki zostały opróżnione do klienta, gdy wynik akcji zgłasza wyjątek, nie ma niezawodnego mechanizmu wysyłania kodu błędu.
W przypadku elementu IAsyncResultFilterwywołanie metody await next
na obiekcie ResultExecutionDelegate wykonuje wszystkie kolejne filtry wyników i wynik akcji. Aby zwarć, ustaw wartość ResultExecutingContext.Cancel true
i nie wywołaj metody ResultExecutionDelegate
:
public class MyAsyncResponseFilter : IAsyncResultFilter
{
public async Task OnResultExecutionAsync(ResultExecutingContext context,
ResultExecutionDelegate next)
{
if (!(context.Result is EmptyResult))
{
await next();
}
else
{
context.Cancel = true;
}
}
}
Struktura zapewnia abstrakcję ResultFilterAttribute
, która może być podklasowana. Pokazana wcześniej klasa AddHeaderAttribute jest przykładem atrybutu filtru wyników.
IAlwaysRunResultFilter i IAsyncAlwaysRunResultFilter
Interfejsy IAlwaysRunResultFilter i IAsyncAlwaysRunResultFilter deklarują implementację uruchamianą IResultFilter dla wszystkich wyników akcji. Obejmuje to wyniki akcji wygenerowane przez:
- Filtry autoryzacji i filtry zasobów, które są zwarcie.
- Filtry wyjątków.
Na przykład następujący filtr zawsze jest uruchamiany i ustawia wynik akcji (ObjectResult) z kodem stanu jednostki nieprzetworzonej 422, gdy negocjowanie zawartości kończy się niepowodzeniem:
public class UnprocessableResultFilter : Attribute, IAlwaysRunResultFilter
{
public void OnResultExecuting(ResultExecutingContext context)
{
if (context.Result is StatusCodeResult statusCodeResult &&
statusCodeResult.StatusCode == (int) HttpStatusCode.UnsupportedMediaType)
{
context.Result = new ObjectResult("Can't process this!")
{
StatusCode = (int) HttpStatusCode.UnsupportedMediaType,
};
}
}
public void OnResultExecuted(ResultExecutedContext context)
{
}
}
IFilterFactory
IFilterFactory implementuje IFilterMetadata. IFilterFactory
W związku z tym wystąpienie może być używane jako IFilterMetadata
wystąpienie w dowolnym miejscu w potoku filtru. Gdy środowisko uruchomieniowe przygotowuje się do wywołania filtru, próbuje rzutować go do elementu IFilterFactory
. Jeśli rzutowanie powiedzie się, metoda jest wywoływana w CreateInstance celu utworzenia wywoływanego IFilterMetadata
wystąpienia. Zapewnia to elastyczny projekt, ponieważ dokładny potok filtru nie musi być jawnie ustawiany podczas uruchamiania aplikacji.
IFilterFactory.IsReusable
:
- Jest wskazówką fabryki, że wystąpienie filtru utworzone przez fabrykę może być ponownie używane poza zakresem żądania, w ramach którego został utworzony.
- Nie należy używać z filtrem, który zależy od usług z okresem istnienia innym niż pojedynczy.
Środowisko uruchomieniowe ASP.NET Core nie gwarantuje:
- Zostanie utworzone pojedyncze wystąpienie filtru.
- Filtr nie zostanie ponownie zażądany z kontenera DI w późniejszym czasie.
Ostrzeżenie
Skonfiguruj wartość tak, aby zwracać IFilterFactory.IsReusable true
tylko wtedy, gdy źródło filtrów jest jednoznaczne, filtry są bezstanowe, a filtry są bezpieczne do użycia w wielu żądaniach HTTP. Na przykład nie zwracaj filtrów z di, które są zarejestrowane jako zakres lub przejściowe, jeśli IFilterFactory.IsReusable
zwraca wartość true
.
IFilterFactory
można zaimplementować przy użyciu implementacji atrybutów niestandardowych jako innego podejścia do tworzenia filtrów:
public class AddHeaderWithFactoryAttribute : Attribute, IFilterFactory
{
// Implement IFilterFactory
public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
{
return new InternalAddHeaderFilter();
}
private class InternalAddHeaderFilter : IResultFilter
{
public void OnResultExecuting(ResultExecutingContext context)
{
context.HttpContext.Response.Headers.Add(
"Internal", new string[] { "My header" });
}
public void OnResultExecuted(ResultExecutedContext context)
{
}
}
public bool IsReusable
{
get
{
return false;
}
}
}
Filtr jest stosowany w następującym kodzie:
[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.");
}
}
Przetestuj powyższy kod, uruchamiając przykład pobierania:
- Wywołaj narzędzia deweloperskie F12.
- Przejdź do
https://localhost:5001/Sample/HeaderWithFactory
.
Narzędzia deweloperskie F12 wyświetlają następujące nagłówki odpowiedzi dodane przez przykładowy kod:
- autor:
Rick Anderson
- globaladdheader:
Result filter added to MvcOptions.Filters
- wewnętrzny:
My header
Powyższy kod tworzy wewnętrzny nagłówek odpowiedzi: My header
.
IFilterFactory zaimplementowany w atrybucie
Filtry implementujące IFilterFactory
są przydatne w przypadku filtrów, które:
- Nie wymagaj przekazywania parametrów.
- Mieć zależności konstruktora, które muszą być wypełnione przez di.
TypeFilterAttribute implementuje IFilterFactory. IFilterFactory
Uwidacznia metodę CreateInstance IFilterMetadata tworzenia wystąpienia. CreateInstance
ładuje określony typ z kontenera usług (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");
}
}
}
Poniższy kod przedstawia trzy podejścia do zastosowania elementu [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");
}
W poprzednim kodzie dekorowanie metody za [SampleActionFilter]
pomocą metody jest preferowanym podejściem do stosowania metody SampleActionFilter
.
Używanie oprogramowania pośredniczącego w potoku filtru
Filtry zasobów działają jak oprogramowanie pośredniczące, które otaczają wykonywanie wszystkich elementów, które są dostępne później w potoku. Jednak filtry różnią się od oprogramowania pośredniczącego, ponieważ są one częścią środowiska uruchomieniowego, co oznacza, że mają dostęp do kontekstu i konstrukcji.
Aby użyć oprogramowania pośredniczącego jako filtru, utwórz typ z metodą określającą Configure
oprogramowanie pośredniczące do wstrzykiwania do potoku filtru. W poniższym przykładzie użyto oprogramowania pośredniczącego lokalizacji do ustanowienia bieżącej kultury dla żądania:
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);
}
}
Użyj polecenia , MiddlewareFilterAttribute aby uruchomić oprogramowanie pośredniczące:
[Route("{culture}/[controller]/[action]")]
[MiddlewareFilter(typeof(LocalizationPipeline))]
public IActionResult CultureFromRouteData()
{
return Content(
$"CurrentCulture:{CultureInfo.CurrentCulture.Name},"
+ $"CurrentUICulture:{CultureInfo.CurrentUICulture.Name}");
}
Filtry oprogramowania pośredniczącego są uruchamiane na tym samym etapie potoku filtru co filtry zasobów, przed powiązaniem modelu i po pozostałej części potoku.
Bezpieczeństwo wątkowe
Podczas przekazywania wystąpienia filtru do Add
elementu , a nie do Type
elementu , filtr jest pojedynczym elementem i nie jest bezpieczny wątkowo.
Następne akcje
- Zobacz Metody filtrowania stronRazor.
- Aby eksperymentować z filtrami, pobierać, testować i modyfikować przykład usługi GitHub.
Opinia
https://aka.ms/ContentUserFeedback.
Dostępne już wkrótce: W 2024 r. będziemy stopniowo wycofywać zgłoszenia z serwisu GitHub jako mechanizm przesyłania opinii na temat zawartości i zastępować go nowym systemem opinii. Aby uzyskać więcej informacji, sprawdź:Prześlij i wyświetl opinię dla