C# Academy · Урок

Обзор конвейера ASP.NET Core

Разберитесь, как запросы проходят через конвейер промежуточного ПО и HttpContext и как формируются ответы.

Урок 1 из 412 шагов

«Обзор конвейера ASP.NET Core» — бесплатный урок C# Academy на CoddyKit. Это урок 1 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения C# Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс C# Academy содержит 4 уроков всего.

Конвейер обработки запросов

Каждый HTTP-запрос в ASP.NET Core проходит через конвейер промежуточного ПО. Каждый компонент промежуточного ПО может просматривать, изменять, досрочно завершать или передавать запрос следующему компоненту. Понимание этого конвейера необходимо для создания приложений ASP.NET Core.

HttpContext: оболочка запроса

HttpContext содержит всё, что относится к запросу и ответу: заголовки, файлы cookie, тело, утверждения пользователя, сведения о соединении и токен отмены. Все компоненты промежуточного ПО работают с ним.

app.Use(async (context, next) =>
{
    // Read request info
    var method  = context.Request.Method;
    var path    = context.Request.Path;
    var headers = context.Request.Headers;
    var user    = context.User.Identity?.Name;

    // Write to response
    context.Response.Headers.Append("X-Processed-By", "MyMiddleware");

    await next(context); // forward to next middleware
});

Порядок регистрации промежуточного ПО

Промежуточное ПО выполняется в порядке регистрации. Ответ разворачивается в обратном порядке, как стек. Порядок критически важен: аутентификация должна выполняться до авторизации, а маршрутизация — до сопоставления с конечной точкой.

// Typical order for an ASP.NET Core app:
app.UseExceptionHandler("/error"); // outermost — catches everything
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors("policy");
app.UseAuthentication();  // must be before Authorization
app.UseAuthorization();
app.UseRateLimiter();
app.UseOutputCache();
// MapHub, MapControllers, MapGet etc.
app.Run();

Use, Run и Map

Промежуточное ПО добавляется тремя методами: Use вызывает следующий компонент, Run завершает конвейер и никогда не вызывает следующий компонент, а Map создаёт ветвь на основе пути.

// Use: passes to next
app.Use(async (ctx, next) =>
{
    Console.WriteLine("Before");
    await next(ctx);
    Console.WriteLine("After");
});

// Run: terminal — no next
app.Run(async ctx =>
    await ctx.Response.WriteAsync("Terminal"));

// Map: branch on path prefix
app.Map("/api", apiApp =>
    apiApp.Run(async ctx =>
        await ctx.Response.WriteAsync("API branch")));

Тела запросов и ответов

Тела запросов и ответов представлены потоками. Тело запроса можно прочитать только один раз — включите буферизацию тела запроса, если нужно читать его несколько раз, например в нескольких компонентах промежуточного ПО.

app.Use(async (ctx, next) =>
{
    // Enable buffering so body can be read multiple times
    ctx.Request.EnableBuffering();

    using var reader = new StreamReader(
        ctx.Request.Body,
        leaveOpen: true);
    var body = await reader.ReadToEndAsync();
    ctx.Request.Body.Position = 0; // rewind for next middleware

    Console.WriteLine($"Body: {body}");
    await next(ctx);
});

Досрочное завершение конвейера

Компонент промежуточного ПО может досрочно завершить конвейер, записав ответ и НЕ вызвав next. Это полезно для шлюзов аутентификации, проверок работоспособности или страниц технического обслуживания.

app.Use(async (ctx, next) =>
{
    if (ctx.Request.Path == "/maintenance")
    {
        ctx.Response.StatusCode = 503;
        await ctx.Response.WriteAsync("Service under maintenance");
        return; // short-circuit — next is NOT called
    }
    await next(ctx);
});

Интерфейс промежуточного ПО

Для промежуточного ПО на основе класса, которому требуется DI, реализуйте IMiddleware. Зарегистрируйте реализацию в DI и используйте app.UseMiddleware<T>().

public class RequestTimingMiddleware : IMiddleware
{
    private readonly ILogger<RequestTimingMiddleware> _logger;
    public RequestTimingMiddleware(ILogger<RequestTimingMiddleware> l) => _logger = l;

    public async Task InvokeAsync(HttpContext ctx, RequestDelegate next)
    {
        var sw = System.Diagnostics.Stopwatch.StartNew();
        await next(ctx);
        _logger.LogInformation("{Method} {Path}: {Ms}ms",
            ctx.Request.Method, ctx.Request.Path, sw.ElapsedMilliseconds);
    }
}

// Register:
builder.Services.AddTransient<RequestTimingMiddleware>();
app.UseMiddleware<RequestTimingMiddleware>();

Обычные классы промежуточного ПО

Другой вариант — создать класс с методом InvokeAsync(HttpContext, RequestDelegate). Платформа внедряет зависимости, включая следующий делегат, через конструктор.

public class ApiKeyMiddleware
{
    private readonly RequestDelegate _next;
    private readonly string _key;

    // RequestDelegate injected by framework
    public ApiKeyMiddleware(RequestDelegate next, IConfiguration cfg)
    {
        _next = next;
        _key = cfg["ApiKey"] ?? "";
    }

    public async Task InvokeAsync(HttpContext ctx)
    {
        if (ctx.Request.Headers["X-Api-Key"] != _key)
        {
            ctx.Response.StatusCode = 401;
            return;
        }
        await _next(ctx);
    }
}

Изменение ответов после следующего компонента

Код после await next(ctx) выполняется после начала записи ответа. Можно добавить заголовки ответа до передачи управления следующему компоненту, но после этого нельзя изменить код состояния или тело ответа.

app.Use(async (ctx, next) =>
{
    // Before: add response headers (safe)
    ctx.Response.OnStarting(() =>
    {
        ctx.Response.Headers.Append("X-Request-Id",
            Guid.NewGuid().ToString("N"));
        return Task.CompletedTask;
    });

    await next(ctx);

    // After: response may already be sent
    // SAFE: logging, metrics
    // UNSAFE: changing status code or writing body
    Console.WriteLine($"Response status: {ctx.Response.StatusCode}");
});

Практический пример: промежуточное ПО для идентификатора корреляции

Рабочее промежуточное ПО для идентификатора корреляции добавляет идентификатор трассировки к каждому запросу, поддерживая распределённую трассировку и журналирование.

public class CorrelationIdMiddleware
{
    private readonly RequestDelegate _next;
    private const string Header = "X-Correlation-Id";

    public CorrelationIdMiddleware(RequestDelegate next) => _next = next;

    public async Task InvokeAsync(HttpContext ctx)
    {
        var correlationId = ctx.Request.Headers[Header].FirstOrDefault()
            ?? Guid.NewGuid().ToString("N");

        ctx.Items["CorrelationId"] = correlationId;

        ctx.Response.OnStarting(() =>
        {
            ctx.Response.Headers.Append(Header, correlationId);
            return Task.CompletedTask;
        });

        using (Serilog.Context.LogContext.PushProperty("CorrelationId", correlationId))
            await _next(ctx);
    }
}

Быстрая проверка

В чём разница между методами Use() и Run() при добавлении промежуточного ПО?

Повторение: обзор конвейера ASP.NET Core

Основные выводы:

  • Промежуточное ПО выполняется в порядке регистрации, а ответы разворачиваются в обратном порядке
  • Use → вызывает следующий компонент; Run → завершает конвейер; Map → создаёт ветвь на основе пути
  • HttpContext содержит запрос, ответ, пользователя и токен отмены
  • Включайте буферизацию, чтобы читать тело запроса несколько раз
  • Интерфейс IMiddleware позволяет использовать промежуточное ПО на основе классов с DI
  • Добавляйте заголовки ответа через OnStarting(); не изменяйте состояние после передачи управления следующему компоненту
Можно начать бесплатно

Изучай C# с ИИ-репетитором — бесплатно

Пиши и запускай код прямо в браузере, получай мгновенную помощь от ИИ-репетитора 24/7 и продолжи учиться на сайте или в приложении.

Курсы
93
Уроки
346

Часто задаваемые вопросы

Урок «Обзор конвейера ASP.NET Core» бесплатный?

Да — полный текст урока «Обзор конвейера ASP.NET Core» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс C# Academy, подпишись на CoddyKit PRO. Курс C# Academy содержит 4 уроков всего.

Чему я научусь в уроке «Обзор конвейера ASP.NET Core»?

Разберитесь, как запросы проходят через конвейер промежуточного ПО и HttpContext и как формируются ответы. Ты практикуешь C# Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать C# Academy?

Предыдущий опыт не требуется. C# Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 1 из 4.

Сколько времени занимает урок «Обзор конвейера ASP.NET Core»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке C# Academy?

Да. Каждый урок C# Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Обзор конвейера ASP.NET Core
  2. Написание собственного промежуточного ПО
  3. Досрочное завершение и ветвление
  4. Порядок промежуточного ПО и встроенные компоненты
← Назад к C# Academy