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

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

Ответ

В 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)