В какой момент выполняется логика в middleware в ASP.NET Core?

«В какой момент выполняется логика в middleware в ASP.NET Core?» — вопрос из категории ASP.NET Core, который задают на 25% собеседований C# Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Логика middleware выполняется в строгом порядке, определяемом конвейером HTTP-запроса (pipeline). Порядок регистрации компонентов в Startup.Configure или Program.cs критичен.

Ключевые моменты:

  1. Последовательное выполнение: Запрос проходит через middleware один за другим, начиная с первого зарегистрированного.
  2. Контроль потока с next(): Каждый middleware решает, передать ли управление следующему компоненту, вызвав await next.Invoke() (или await next()).
  3. Два этапа выполнения:
    • Логика ДО вызова next() — выполняется при прохождении запроса "вниз" по конвейеру (например, аутентификация, логирование).
    • Логика ПОСЛЕ вызова next() — выполняется при обратном проходе ответа "вверх" по конвейеру (например, обработка ошибок, добавление заголовков).
  4. Short-circuiting: Если middleware НЕ вызывает next(), он завершает конвейер, и последующие компоненты не выполняются (пример — middleware для статических файлов или аутентификации при неудаче).

Пример кода, иллюстрирующий поток:

app.Use(async (context, next) =>
{
    // Этап 1: Логика ДО следующего middleware
    Console.WriteLine("Middleware 1: Before next");
    context.Items["StartTime"] = DateTime.UtcNow;

    await next(); // Передача управления следующему middleware в конвейере

    // Этап 2: Логика ПОСЛЕ выполнения остального конвейера
    var duration = DateTime.UtcNow - (DateTime)context.Items["StartTime"];
    Console.WriteLine($"Middleware 1: After next. Request took {duration.TotalMilliseconds}ms");
});

app.Use(async (context, next) =>
{
    Console.WriteLine("Middleware 2: Before next");
    await next();
    Console.WriteLine("Middleware 2: After next");
});

// Output для запроса:
// Middleware 1: Before next
// Middleware 2: Before next
// ... (выполнение конечной точки)
// Middleware 2: After next
// Middleware 1: After next

Практическое правило: Порядок регистрации должен соответствовать логическим зависимостям (например, Middleware для обработки исключений регистрируется первым, чтобы ловить ошибки из всех последующих компонентов).