Ответ
Фильтры в ASP.NET Core — это компоненты, которые выполняют код до или после определенных этапов конвейера обработки запроса (pipeline). Они позволяют инкапсулировать сквозную функциональность (cross-cutting concerns), такую как авторизация, логирование, валидация, кэширование.
Фильтры выполняются в строгом порядке, известном как Pipeline фильтров:
- Authorization Filters → 2. Resource Filters → 3. Action Filters → 4. Exception Filters → 5. Result Filters
Основные типы фильтров:
1. Authorization Filters (IAuthorizationFilter, IAsyncAuthorizationFilter)
- Цель: Определить, авторизован ли пользователь для доступа к ресурсу.
- Выполняется: Первыми, до привязки модели и выполнения действия.
- Пример:
[Authorize],[AllowAnonymous]. - Зачем свой? Для реализации кастомной политики доступа (например, на основе роли и региона).
2. Resource Filters (IResourceFilter, IAsyncResourceFilter)
- Цель: Обработка запроса до и после остальных фильтров в конвейере. Идеальны для кэширования или валидации состояния ресурса.
- Выполняется: После Authorization, но до Model Binding. Имеют метод
OnResourceExecuting(до) иOnResourceExecuted(после). - Пример использования: Короткое замыкание конвейера (early exit) для возврата закэшированного результата.
public class CustomCacheResourceFilter : IAsyncResourceFilter
{
public async Task OnResourceExecutionAsync(ResourceExecutingContext context, ResourceExecutionDelegate next)
{
// 1. ДО выполнения остального конвейера: проверяем кэш
var cacheKey = GenerateCacheKey(context.HttpContext.Request);
if (_cache.TryGetValue(cacheKey, out var cachedResult))
{
context.Result = cachedResult as IActionResult; // Короткое замыкание
return;
}
// 2. Позволяем конвейеру выполниться
var executedContext = await next();
// 3. ПОСЛЕ выполнения: сохраняем результат в кэш
if (executedContext.Result is OkObjectResult okResult)
{
_cache.Set(cacheKey, okResult.Value, TimeSpan.FromMinutes(5));
}
}
}
3. Action Filters (IActionFilter, IAsyncActionFilter)
- Цель: Логика, специфичная для действия контроллера. Выполняются до и после вызова метода действия, но после привязки модели.
- Использование: Валидация модели, логирование параметров, изменение аргументов или результата действия.
- Глобальный пример:
[ServiceFilter(typeof(LoggingActionFilter))].
4. Exception Filters (IExceptionFilter, IAsyncExceptionFilter)
- Цель: Глобальная обработка необработанных исключений, возникших в контроллере, фильтрах или привязке модели.
- Выполняется: Только при возникновении исключения.
- Важно: Не перехватывают исключения в Middleware или при генерации результата (для этого есть
UseExceptionHandlermiddleware). - Пример: Отправка специфичных ошибок API в определенном формате (JSON).
5. Result Filters (IResultFilter, IAsyncResultFilter)
- Цель: Логика, выполняемая до и после выполнения результата действия (например, перед рендерингом представления).
- Выполняется: После метода действия, но до выполнения
IActionResult(вызоваView()илиJson()). - Использование: Изменение HTTP-заголовков, форматирование ответа, логирование результата.
Способы применения фильтров:
- На уровне действия:
[TypeFilter(typeof(MyActionFilter))]на методе контроллера. - На уровне контроллера: Атрибут применяется ко всем действиям в контроллере.
- Глобально: Регистрация в
Program.csилиStartup.ConfigureServices:services.AddControllers(options => { options.Filters.Add(new MyGlobalFilter()); // Экземпляр options.Filters.Add<MyGlobalFilter>(); // Тип (будет разрешен через DI) });
Ключевое правило: Фильтры, зарегистрированные глобально, применяются ко всем действиям. Фильтры на контроллере/действии выполняются в порядке: глобальные → контроллер → действие (для OnActionExecuting), и в обратном порядке для OnActionExecuted.
Ответ 18+ 🔞
А, фильтры в ASP.NET Core! Ну это ж, блядь, как слоёный пирог, только для запросов. Слушай, сейчас разложу по полочкам, чтобы даже мартышке было понятно.
Вот представь: к тебе на сервер приходит запрос — как клиент в госучреждение. Сначала его на входе проверяют (авторизация), потом смотрят, все ли документы принёс (валидация), потом уже по кабинетам гоняют, и в конце либо результат выдают, либо выносят мозг с ошибкой. Так вот фильтры — это как раз те самые процедурные этапы, через которые каждый запрос прёт.
Порядок — это пиздец какой важный, запомни раз и навсегда:
- Фильтры авторизации — тебя вообще пускать внутрь? Нет прав — иди нахуй, дальше даже не начинаем.
- Ресурсные фильтры — типа "подожди, а может, мы уже это где-то делали?" Идеально для кэша, чтобы лишний раз не париться.
- Фильтры действий — вот тут уже конкретика: перед самым вызовом твоего метода и сразу после.
- Фильтры исключений — если где-то выше по цепочке пиздец случился, они пытаются это прикрыть.
- Фильтры результатов — последний штрих: прямо перед тем, как отправить ответ клиенту.
Ну а теперь подробнее, с приправами:
1. Авторизация (IAuthorizationFilter)
Это как вышибала в клубе. "Ты кто такой? А, нет тебя в списке — свободен". Выполняется самым первым, ещё до того, как мы вообще понимаем, что за запрос пришёл. Стандартный [Authorize] — это оно и есть. Свой пишешь, если тебе мало просто проверки на логин — например, нужно ещё и чтобы пользователь из нужного региона был, и не полупидор.
2. Ресурсные фильтры (IResourceFilter)
О, это хитрая жопа! Они выполняются ДО всего основного конвейера (прямо после авторизации) и ПОСЛЕ него. Идеально, чтобы срезать углы. Например, если у тебя в кэше уже лежит готовый ответ — зачем вообще выполнять действие, бензин жечь? Вот тебе живой пример, смотри:
public class CustomCacheResourceFilter : IAsyncResourceFilter
{
public async Task OnResourceExecutionAsync(ResourceExecutingContext context, ResourceExecutionDelegate next)
{
// 1. Сначала: проверяем, нет ли уже всего готового?
var cacheKey = GenerateCacheKey(context.HttpContext.Request);
if (_cache.TryGetValue(cacheKey, out var cachedResult))
{
context.Result = cachedResult as IActionResult; // Всё, приехали, дальше не идём
return;
}
// 2. Пускаем запрос по всему конвейеру (действие, базы и прочую хуйню)
var executedContext = await next();
// 3. После того как всё посчитали — кладём в кэш на будущее
if (executedContext.Result is OkObjectResult okResult)
{
_cache.Set(cacheKey, okResult.Value, TimeSpan.FromMinutes(5));
}
}
}
Видишь магию? Если нашли в кэше — сразу return, и весь оставшийся pipeline, включая твой тяжёлый метод в контроллере, даже не запустится. Экономия — ебать просто!
3. Фильтры действий (IActionFilter)
Это уже ближе к делу. Выполняются прямо перед вызовом твоего метода и сразу после. Модель уже привязана, параметры готовы — можно, например, проверить, а валидна ли она вообще. Или залогировать, что за аргументы пришли. Или, если результат не нравится, подменить его в OnActionExecuted. Удобно, как карманный пердак.
4. Фильтры исключений (IExceptionFilter)
Ловят всё, что не поймали до этого. Упало исключение в контроллере или в фильтре — они пытаются это обработать и выдать адекватную ошибку, а не просто "500 Internal Server Error, иди нахуй". Но запомни: они НЕ ловят исключения из middleware — для этого есть отдельная, блядь, штука UseExceptionHandler. Не перепутай, а то будешь как дурак.
5. Фильтры результатов (IResultFilter)
Всё, действие выполнилось, результат сформирован (например, View() или Json()), но ещё не отправлен клиенту. Вот тут-то они и работают. Можно добавить какие-нибудь хидеры в ответ, изменить формат, залогировать, что же мы, собственно, отправили. Последний шанс всё подправить.
Куда это всё пихать?
Да куда угодно, блядь! Можно навесить на конкретный метод контроллера — тогда только для него. Можно на весь контроллер — тогда все методы там будут через это проходить. А можно глобально, в настройках — и тогда ВСЕ запросы во ВСЕХ контроллерах будут этим пользоваться. Это как с солью: перебор — и всё испортишь.
Вот так регистрируешь глобально, в Program.cs:
services.AddControllers(options =>
{
options.Filters.Add(new MyGlobalFilter()); // Просто инстанс
options.Filters.Add<MyGlobalFilter>(); // Или через DI, чтобы зависимости инжектить
});
Важное правило про порядок, а то запутаешься: для методов "до" (типа OnActionExecuting) порядок такой: сначала глобальные, потом на контроллере, потом на действии. А для методов "после" (OnActionExecuted) — всё наоборот: сначала действие, потом контроллер, потом глобальные. Как матрёшка, только в разобранном и собранном виде.
Короче, фильтры — это мощно. Позволяют не засирать контроллеры одинаковым кодом, а вынести всю эту рутину в отдельные компоненты. Главное — не увлекаться и понимать, в каком порядке они выстрелят, а то получится "вротберунчик", и будешь дебажить до пенсии.