Какие методы конфигурации конвейера middleware (жизненного цикла запроса) вы знаете в ASP.NET Core?

Ответ

В ASP.NET Core конвейер обработки HTTP-запроса (pipeline) строится с помощью методов расширения для IApplicationBuilder. Эти методы добавляют компоненты middleware, которые выполняются последовательно. Вот ключевые методы:

1. Use — Добавление общего middleware Добавляет middleware, который может обработать запрос и/или передать управление следующему компоненту в конвейере.

app.Use(async (HttpContext context, RequestDelegate next) =>
{
    // Логика, выполняемая ДО следующего middleware (например, логирование, установка заголовков)
    LogRequest(context.Request.Path);

    await next(context); // Вызов следующего middleware в цепочке

    // Логика, выполняемая ПОСЛЕ следующего middleware (например, модификация ответа)
    LogResponse(context.Response.StatusCode);
});

2. Run — Терминальный middleware Добавляет middleware, который завершает выполнение конвейера (не вызывает next). Все middleware, зарегистрированные после Run, никогда не выполнятся для данного пути.

app.Run(async context =>
{
    await context.Response.WriteAsync("Это конечная точка. Конвейер завершен здесь.");
});
// Этот middleware уже не будет вызван для запроса, попавшего в `Run` выше.
app.Use(...);

3. Map — Ветвление конвейера по пути запроса Создает отдельную ветку конвейера, которая выполняется только если путь запроса начинается с указанного сегмента.

app.Map("/api", apiApp =>
{
    // Этот вложенный конвейер сработает только для путей, начинающихся с /api
    apiApp.UseMiddleware<ApiAuthenticationMiddleware>();
    apiApp.UseRouting();
    apiApp.UseEndpoints(endpoints => endpoints.MapControllers());
});

app.Map("/admin", adminApp =>
{
    // Отдельный конвейер для админки
    adminApp.UseMiddleware<AdminAuthMiddleware>();
    adminApp.UseStaticFiles();
    adminApp.Run(async ctx => await ctx.Response.WriteAsync("Admin Area"));
});

4. MapWhen — Ветвление по условию Более гибкий метод, который создает ветку на основе произвольного условия (предиката) над HttpContext.

app.MapWhen(context => context.Request.Query.ContainsKey("debug"), debugApp =>
{
    // Этот конвейер выполнится только если в запросе есть query-параметр 'debug'
    debugApp.UseMiddleware<DebugInfoMiddleware>();
});

5. UseWhen — Условное выполнение middleware с возвратом Позволяет выполнить дополнительный middleware на основе условия, после которого выполнение возвращается в основной конвейер. В отличие от Map, не создает изолированную ветку.

app.UseWhen(context => context.Request.Headers.ContainsKey("X-Forwarded-For"), appBuilder =>
{
    appBuilder.UseMiddleware<ReverseProxyHeaderMiddleware>();
});
// Middleware, зарегистрированные здесь, выполнятся ВСЕГДА, после ReverseProxyHeaderMiddleware (если условие сработало).
app.UseRouting();

Принцип работы: Конвейер строится в Program.cs или Startup.Configure. Порядок регистрации middleware критически важен. Middleware выполняется в порядке регистрации для входящего запроса и в обратном порядке для исходящего ответа (если используется await next()), образуя паттерн «вложенности» (nested pattern).

Типичная последовательность в реальном приложении:

app.UseExceptionHandler("/error"); // Глобальный обработчик исключений (первым, чтобы ловить все)
app.UseHttpsRedirection();
app.UseStaticFiles(); // Обслуживание статических файлов
app.UseRouting(); // Маршрутизация
app.UseAuthentication(); // Аутентификация
app.UseAuthorization(); // Авторизация
app.MapControllers(); // Сопоставление контроллеров (терминальный для API)
app.MapRazorPages(); // Сопоставление Razor Pages (терминальный для UI)

Ответ 18+ 🔞

А, ну это же классика, блядь! Сейчас объясню на пальцах, как этот ваш ASP.NET Core запросы обрабатывает, чтобы даже чайник понял.

Представь себе, что HTTP-запрос — это как мужик, который пришёл в баню. А конвейер middleware — это процесс его обслуживания. Сначала его в предбаннике раздевают (UseExceptionHandler), потом шайкой обливают (UseHttpsRedirection), в парилку загоняют (UseStaticFiles) и так далее. Порядок — это всё, ёпта! Если сначала в парилку загнать, а потом раздевать — будет пиздец.

1. Use — обычный пацан в цепочке Это как работяга, который может и сам поработать, и следующему передать. Главное — не забыть next() вызвать, а то вся цепочка нахуй сломается.

app.Use(async (HttpContext context, RequestDelegate next) =>
{
    // Тут делаем что-то ДО следующего (типа, "ой, какой клиент зашёл")
    Console.WriteLine($"Пришёл запрос на {context.Request.Path}, бля");

    await next(context); // Кричим "следующий, готовься!"

    // А тут уже ПОСЛЕ (типа, "ой, что-то он быстро свалил")
    Console.WriteLine($"Ушёл с кодом {context.Response.StatusCode}");
});

2. Run — терминатор, конец пути Это тот мужик в бане, который говорит: "Всё, братан, дальше не идёшь. Садись на полок, получай ответ и проваливай". После него уже никто не работает.

app.Run(async context =>
{
    await context.Response.WriteAsync("Всё, пиши пропало. Ответ тут и точка.");
});
// Всё что ниже — уже не выполнится, как ни старайся.

3. Map — отдельная VIP-зона по пути Если запрос пришёл по пути /api — его отправляют в "апишную" баню. Если по /admin — в "админскую". У каждой свои процедуры.

app.Map("/api", apiApp =>
{
    // Сюда только апишники заходят
    apiApp.UseMiddleware<ApiAuthMiddleware>(); // Проверяют пропуск
    apiApp.UseRouting();
    apiApp.MapControllers(); // Собственно, сам бар
});

app.Map("/admin", adminApp =>
{
    // А тут админы царствуют
    adminApp.UseMiddleware<SuperStrictAuthMiddleware>(); // Тут вообще паспорт спрашивают
    adminApp.Run(async ctx => await ctx.Response.WriteAsync("Ты в админке, не нассь тут"));
});

4. MapWhen — ветвление по любой прихоти Не только по пути, а по любому условию. Например, если в запросе есть параметр debug=true — включаем отладчик.

app.MapWhen(context => context.Request.Query.ContainsKey("debug"), debugApp =>
{
    // О, тут дебаг! Показываем все кишки
    debugApp.UseMiddleware<ShowMeEverythingMiddleware>();
});

5. UseWhen — временное отклонение Он не создаёт отдельную ветку навсегда, а как бы говорит: "Если условие выполнилось — заскочим сюда на секунду, сделаем дела и вернёмся обратно в общий поток".

app.UseWhen(context => context.Request.Headers.ContainsKey("X-Forwarded-For"), appBuilder =>
{
    appBuilder.UseMiddleware<HandleProxyMiddleware>(); // Обрабатываем заголовок прокси
});
// А потом всё равно идём дальше по общему конвейеру
app.UseRouting();

Самое важное, блядь: порядок регистрации — это святое! Это как инструкция к сборке шкафа — если винты в начале прикрутить, а потом доски собирать, получится хуйня, а не шкаф.

Вот типичный адекватный порядок в нормальном приложении:

app.UseExceptionHandler("/error"); // Первым делом — глобальный отлов всех косяков
app.UseHttpsRedirection();        // Перегоняем всех с HTTP на HTTPS
app.UseStaticFiles();             // Раздаём статику (картинки, скрипты)
app.UseRouting();                 // Включаем маршрутизацию ("ты куда путь держишь?")
app.UseAuthentication();          // "Предъяви документы"
app.UseAuthorization();           // "А теперь покажи, что тебе можно в эту комнату"
app.MapControllers();             // API-контроллеры
app.MapRazorPages();              // Razor Pages для веба

Запомни: конвейер работает как матрёшка. Запрос идёт сверху вниз через все Use, пока не упрётся в Run или конечную точку. А потом ответ идёт обратно снизу вверх через все те же Use, но уже через их "после-Next" часть. Красота, ёпта!