C# Academy · Lekcja

Przegląd potoku ASP.NET Core

Poznaj przepływ żądań przez potok middleware i HttpContext oraz sposób budowania odpowiedzi.

Lekcja 1 z 412 kroki

Przegląd potoku ASP.NET Core to bezpłatna lekcja C# Academy na CoddyKit. To lekcja 1 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej C# Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs C# Academy zawiera 4 lekcji w sumie.

Potok obsługi żądań

Każde żądanie HTTP w ASP.NET Core przechodzi przez potok middleware. Każdy element middleware może sprawdzać i modyfikować żądanie, zatrzymać jego obsługę lub przekazać je do następnego komponentu. Zrozumienie tego potoku ma fundamentalne znaczenie podczas tworzenia aplikacji ASP.NET Core.

HttpContext: koperta żądania

HttpContext zawiera wszystkie informacje o żądaniu i odpowiedzi: nagłówki, pliki cookie, treść, oświadczenia użytkownika, informacje o połączeniu oraz token anulowania. Wszystkie elementy middleware działają na tym obiekcie.

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
});

Kolejność rejestrowania middleware

Elementy middleware są uruchamiane w kolejności, w której zostały zarejestrowane. Odpowiedź jest rozwijana w odwrotnej kolejności, podobnie jak stos. Kolejność ma kluczowe znaczenie: uwierzytelnianie musi działać przed autoryzacją, a routing przed dopasowaniem endpointu.

// 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 i Map

Do dodawania middleware służą trzy metody: Use (wywołuje następny element), Run (kończy potok i nigdy nie wywołuje następnego elementu) oraz Map (tworzy rozgałęzienie na podstawie ścieżki).

// 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")));

Treść żądania i odpowiedzi

Treść żądania i odpowiedzi jest reprezentowana przez strumienie. Odczyt treści żądania można wykonać tylko raz — jeśli trzeba odczytać ją wielokrotnie, należy włączyć buforowanie treści żądania, na przykład w kilku elementach middleware.

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);
});

Zatrzymywanie potoku

Middleware może zatrzymać potok, zapisując odpowiedź i NIE wywołując next. Jest to przydatne w przypadku bram uwierzytelniania, testów kondycji lub stron informujących o konserwacji.

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);
});

Interfejs IMiddleware

W przypadku klasowego middleware wymagającego DI należy zaimplementować IMiddleware. Implementację należy zarejestrować w DI i użyć 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>();

Konwencjonalne klasy middleware

Alternatywnie można utworzyć klasę z metodą InvokeAsync(HttpContext, RequestDelegate). Framework wstrzykuje zależności, w tym następny delegat, za pośrednictwem konstruktora.

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);
    }
}

Modyfikowanie odpowiedzi po wywołaniu next

Kod znajdujący się po await next(ctx) wykonuje się po rozpoczęciu zapisywania odpowiedzi. Nagłówki odpowiedzi można dodać przed wywołaniem next, ale później nie można już zmienić kodu stanu ani treści odpowiedzi.

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}");
});

Praktyczny przykład: middleware identyfikatora korelacji

Produkcyjny element middleware identyfikatora korelacji dodaje identyfikator śledzenia do każdego żądania na potrzeby śledzenia rozproszonego i rejestrowania.

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);
    }
}

Szybki test

Jaka jest różnica między app.Use() a app.Run() podczas dodawania middleware?

Podsumowanie: przegląd potoku ASP.NET Core

Najważniejsze informacje:

  • Middleware jest uruchamiane w kolejności rejestrowania, a odpowiedzi są rozwijane w odwrotnej kolejności
  • Use → wywołuje następny element; Run → kończy potok; Map → tworzy rozgałęzienie na podstawie ścieżki
  • HttpContext zawiera żądanie, odpowiedź, użytkownika i token anulowania
  • Należy włączyć buforowanie, aby wielokrotnie odczytywać treść żądania
  • Interfejs IMiddleware umożliwia tworzenie klasowego middleware przyjaznego dla DI
  • Nagłówki odpowiedzi należy dodawać za pomocą OnStarting(); po wywołaniu next nie należy zmieniać kodu stanu
Bezpłatny start

Ucz się C# dzięki korepetycjom AI — za darmo

Pisz i uruchamiaj kod w przeglądarce, otrzymuj natychmiastową pomoc od korepetytora AI dostępnego 24/7 i kontynuuj naukę w sieci lub w aplikacji.

Kursy
93
Lekcje
346

Często zadawane pytania

Czy lekcja „Przegląd potoku ASP.NET Core” jest bezpłatna?

Tak — pełny tekst „Przegląd potoku ASP.NET Core” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu C# Academy, przejdź na CoddyKit PRO. Kurs C# Academy zawiera 4 lekcji w sumie.

Co nauczysz się w „Przegląd potoku ASP.NET Core”?

Poznaj przepływ żądań przez potok middleware i HttpContext oraz sposób budowania odpowiedzi. Ćwiczysz C# Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.

Czy potrzebuję doświadczenia, aby zacząć C# Academy?

Nie wymagamy żadnego doświadczenia. C# Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 1 z 4.

Ile czasu zajmuje lekcja „Przegląd potoku ASP.NET Core”?

Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.

Czy mogę pisać i uruchamiać kod w tej lekcji C# Academy?

Tak. Każda lekcja C# Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.

Wszystkie lekcje w tym kursie

  1. Przegląd potoku ASP.NET Core
  2. Pisanie niestandardowego middleware
  3. Skracanie i rozgałęzianie potoku
  4. Kolejność middleware i wbudowane middleware
← Powrót do C# Academy