В чем разница между middleware и фильтром в ASP.NET Core?

Ответ

Middleware и фильтры — это два разных уровня обработки HTTP-запроса в ASP.NET Core с разными областями ответственности.

Middleware

  • Уровень: Низкоуровневый HTTP-конвейер приложения. Работает с HttpContext.
  • Область видимости: Глобальная для всего приложения. Обрабатывает все запросы, проходящие через конвейер.
  • Жизненный цикл: Выполняется в строгом порядке регистрации в Program.cs. Может обработать запрос и завершить конвейер, не передавая управление следующему компоненту (например, UseAuthentication может вернуть 401).
  • Назначение: Логика, не зависящая от фреймворка MVC/Razor Pages: аутентификация, авторизация, статические файлы, кэширование, кастомная обработка заголовков, глобальная обработка ошибок.

Пример кастомного middleware для логирования:

public class LoggingMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger _logger;

    public LoggingMiddleware(RequestDelegate next, ILogger<LoggingMiddleware> logger)
    {
        _next = next;
        _logger = logger;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        _logger.LogInformation("Начало обработки запроса: {Path}", context.Request.Path);
        var sw = Stopwatch.StartNew();

        await _next(context); // Передача управления следующему middleware

        sw.Stop();
        _logger.LogInformation("Запрос {Path} обработан за {ElapsedMs} мс", context.Request.Path, sw.ElapsedMilliseconds);
    }
}
// Регистрация в Program.cs
app.UseMiddleware<LoggingMiddleware>();
// или app.Use(async (context, next) => { ... });

Фильтры (Filters)

  • Уровень: Высокоуровневый конвейер MVC (или Razor Pages/API). Работают в контексте MVC (ActionExecutingContext, ActionExecutedContext).
  • Область видимости: Могут быть глобальными, но чаще применяются к конкретным контроллерам или действиям (action methods) с помощью атрибутов.
  • Жизненный цикл: Выполняются в рамках конвейера MVC (после маршрутизации и привязки модели, но до/после выполнения кода действия). Имеют четкие этапы: Authorization -> Resource -> Action -> Exception -> Result.
  • Назначение: Логика, тесно связанная с MVC: валидация модели, авторизация на уровне действия, кэширование результатов действия, обработка исключений в действиях.

Пример фильтра для валидации модели:

public class ValidateModelAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext context)
    {
        if (!context.ModelState.IsValid)
        {
            // Прерываем выполнение действия, если модель невалидна
            context.Result = new BadRequestObjectResult(context.ModelState);
        }
        base.OnActionExecuting(context);
    }
}
// Применение к действию
[HttpPost]
[ValidateModel] // Фильтр сработает перед вызовом метода Create
public IActionResult Create(Product product) { ... }

Ключевое отличие: Middleware — это фундамент конвейера запросов, не знающий о MVC. Фильтры — это надстройка внутри MVC-фреймворка, имеющая доступ к его контексту (модели, действиям, результатам). Используйте middleware для кросс-резающей логики всего приложения, а фильтры — для логики, специфичной для контроллеров и действий.

Ответ 18+ 🔞

Слушай, вот смотри, чтобы не путаться в этих ваших middleware и фильтрах — это же две абсолютно разные вещи, как борщ и суши. Один на уровне всего приложения крутится, другой — уже внутри самой движухи MVC.

Middleware — это как будто ты на проходной завода стоишь. Каждая машина, каждый пешеход — все через тебя проходят. Ты можешь посмотреть пропуск (аутентификация), сказать «стой, куда прешь?» (авторизация), или просто засечь время въезда (логирование). Ты работаешь с голым HttpContext, тебе похуй, что там дальше будет — MVC, gRPC или статика. Ты можешь запрос вообще дальше не пустить и нахуй отправить с 401-м кодом.

Вот, смотри, как простой middleware для логирования выглядит:

public class LoggingMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger _logger;

    public LoggingMiddleware(RequestDelegate next, ILogger<LoggingMiddleware> logger)
    {
        _next = next;
        _logger = logger;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        _logger.LogInformation("Начало обработки запроса: {Path}", context.Request.Path);
        var sw = Stopwatch.StartNew();

        await _next(context); // Передача управления следующему middleware

        sw.Stop();
        _logger.LogInformation("Запрос {Path} обработан за {ElapsedMs} мс", context.Request.Path, sw.ElapsedMilliseconds);
    }
}
// Регистрация в Program.cs
app.UseMiddleware<LoggingMiddleware>();
// или app.Use(async (context, next) => { ... });

Видишь? Он просто вклинивается в общий поток, делает свое дело и пускает дальше. Без него — нихуя не работает, конвейер рушится.

А теперь Фильтры — это уже внутри цеха, на конвейерной линии. Деталь (запрос) уже дошла до конкретного станка (контроллера). Фильтр — это как мастер ОТК, который проверяет, правильно ли деталь закрепили (валидация модели), или начальник смены, который смотрит, есть ли у рабочего допуск к этому станку (авторизация действия). Он работает в контексте MVC, у него есть доступ к самой модели, к параметрам, к результату.

Вот, например, фильтр, который модель проверяет, чтобы не прилетело какого-то хуйма вместо данных:

public class ValidateModelAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext context)
    {
        if (!context.ModelState.IsValid)
        {
            // Прерываем выполнение действия, если модель невалидна
            context.Result = new BadRequestObjectResult(context.ModelState);
        }
        base.OnActionExecuting(context);
    }
}
// Применение к действию
[HttpPost]
[ValidateModel] // Фильтр сработает перед вызовом метода Create
public IActionResult Create(Product product) { ... }

Короче, запомни так:

  • Middleware — это про ВСЁ приложение. Ставишь один раз в Program.cs и он на каждый запрос срабатывает. Логирование, CORS, сжатие — всё сюда.
  • Фильтры — это про конкретные контроллеры и действия. Вешаешь атрибутом и он работает только там, где надо. Валидация, кэширование ответа, кастомная авторизация для админки — вот это всё.

Если тебе нужно что-то сделать ДО того, как запрос вообще поймут, куда его маршрутизировать — это middleware. Если нужно вмешаться УЖЕ В ПРОЦЕССЕ работы твоего action-метода — это фильтр. Всё просто, ёпта. Не усложняй.