Ответ
В 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" часть. Красота, ёпта!