## Wstęp merytoryczny: Zaawansowana mechanika rurociągu Middleware

W procesie transformacji aplikacji z **.NET Framework 4.8** do nowoczesnego ekosystemu **ASP.NET Core**, dogłębne zrozumienie rurociągu oprogramowania pośredniczącego (Middleware) jest kluczowe. Architektura ta, choć z pozoru prosta, kryje w sobie zaawansowane mechanizmy zarządzania cyklem życia wstrzykiwanych zależności (DI) oraz asynchronicznego przetwarzania strumieni odpowiedzi HTTP. Wymaga to precyzji, zwłaszcza w kontekście współdzielenia stanu żądania i obsługi momentu, w którym odpowiedź zostaje wysłana do klienta.

W niniejszym rozdziale przeanalizujemy precyzyjne zasady instancjonowania Middleware oraz bezpieczne techniki pracy z obiektem `HttpResponse`, eliminując ryzyko błędów architektonicznych i wyjątków w czasie wykonywania.

## Analiza Porównawcza: Metody implementacji Middleware

| **Koncepcja Middleware**                    | **Cykl życia instancji**                               | **Wstrzykiwanie usług o cyklu Scoped / Transient**                                             |
| ------------------------------------------- | ------------------------------------------------------ | ---------------------------------------------------------------------------------------------- |
| **Podejście konwencyjne (Legacy/Standard)** | **Singleton** (tworzony raz podczas budowy rurociągu). | Wstrzykiwane wyłącznie jako parametry metody `Invoke` / `InvokeAsync`.                         |
| **Zależności w konstruktorze (Konwencja)**  | **Singleton**                                          | Próba wstrzyknięcia usługi Scoped do konstruktora prowadzi do antywzorca *Captive Dependency*. |
| **Interfejs `IMiddleware`**                 | **Scoped** (tworzony przez fabrykę per żądanie HTTP).  | Bezpieczne wstrzykiwanie bezpośrednio przez konstruktor.                                       |

## Głębokie Nurkowanie (Deep Dive): Cykl życia i modyfikacja odpowiedzi

### 1. Cykl życia usług w Middleware konwencyjnym
W architekturze ASP.NET Core najczęściej spotykanym podejściem (znanym ze starszych wersji frameworka) jest Middleware oparty na konwencji. Taka klasa jest instancjonowana tylko raz, w momencie wywołania `app.Build()` i konfiguracji serwera Kestrel. Z tego powodu jej konstruktor zachowuje się jak **Singleton**. 

Jeżeli spróbujemy wstrzyknąć do tego konstruktora usługę o cyklu życia *Scoped* (np. kontekst Entity Framework Core), usługa ta zostanie "uwięziona" (Captive Dependency) na cały czas życia aplikacji, co nieuchronnie doprowadzi do wycieków pamięci i wyjątków współbieżności. Aby temu zapobiec, usługi *Scoped* muszą być wstrzykiwane jako parametry sygnatury metody asynchronicznej `InvokeAsync`. Metoda ta jest wywoływana dla każdego żądania HTTP, co pozwala kontenerowi DI na dostarczenie zależności o właściwym, krótkim cyklu życia.

### 2. Stan odpowiedzi HTTP i blokada modyfikacji
Każde wywołanie delegata `await next(context)` przekazuje sterowanie do kolejnego elementu rurociągu, który może wygenerować odpowiedź (np. wyrenderować komponent Blazor Server lub zwrócić plik). Gdy sterowanie wraca do naszego Middleware (po słowie kluczowym `await`), odpowiedź mogła już zostać rozpoczęta (tzw. *Response has started*). 

W środowisku webowym (w przeciwieństwie do synchronicznego zwracania widoków w .NET 4.8), serwer Kestrel może przesyłać odpowiedź w modelu strumieniowym (Streaming) do klienta jeszcze przed zakończeniem przetwarzania całego rurociągu. W takim scenariuszu:
*   Odczyt właściwości takich jak `context.Response.ContentType` może zwrócić nieprecyzyjne dane lub zostać zignorowany, jeśli nagłówki zostały już wysłane do przeglądarki.
*   Próba **modyfikacji** nagłówków HTTP, statusu lub struktury odpowiedzi po jej rozpoczęciu bezwzględnie wyrzuci wyjątek systemowy `InvalidOperationException: Headers are read-only, response has already started`.

## Dobre Praktyki i Antywzorce

*   **Antywzorzec:** Sprawdzanie i modyfikowanie stanu `HttpResponse` po wywołaniu `await next()` bez weryfikacji flagi gotowości.
    *   *Rozwiązanie:* Jeżeli Middleware musi dodać nagłówki lub zmodyfikować typ odpowiedzi, powinien to zrobić przed wywołaniem `next()`. Jeśli modyfikacja zależy od wyniku dalszego przetwarzania, należy użyć zdarzenia `context.Response.OnStarting`, które framework wywoła tuż przed fizycznym wysłaniem nagłówków do klienta.
*   **Antywzorzec (Architektura 4.8):** W aplikacjach `System.Web` globalny stan często przechowywano w klasach statycznych. Przenoszenie tego nawyku do konstruktora konwencyjnego Middleware'u w .NET 10 jest destrukcyjne. 
    *   *Dobra Praktyka:* Stosowanie interfejsu `IMiddleware`. Narzuca on silne typowanie i sprawia, że cała instancja Middleware jest powoływana w cyklu *Scoped* (per żądanie HTTP), umożliwiając bezpieczne korzystanie ze wstrzykiwania przez konstruktor.

## Laboratorium kodu: Bezpieczny PerformanceTrackingMiddleware

Poniższy kod przedstawia ewolucję logiki przechwytującej (znanej z dawnego `HttpModule`), zaimplementowaną z użyciem nowoczesnego interfejsu `IMiddleware`. Zapewnia on pełne bezpieczeństwo cyklu życia dla wstrzykiwanego logera (Scoped/Transient) oraz chroni przed wyjątkami modyfikacji wysłanej odpowiedzi.

```csharp
using System.Diagnostics;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;

namespace ModernApp.Middleware;

// Użycie IMiddleware gwarantuje cykl życia "Scoped" (instancja na każde żądanie HTTP)
public class PerformanceTrackingMiddleware : IMiddleware
{
    private readonly ILogger<PerformanceTrackingMiddleware> _logger;

    // Bezpieczne wstrzykiwanie w konstruktorze - fabryka rozwiązuje to per-request
    public PerformanceTrackingMiddleware(ILogger<PerformanceTrackingMiddleware> logger)
    {
        _logger = logger;
    }

    public async Task InvokeAsync(HttpContext context, RequestDelegate next)
    {
        var stopwatch = Stopwatch.StartNew();

        // Bezpieczna rejestracja logiki przed wysłaniem nagłówków.
        // OnStarting gwarantuje, że kod wykona się DOKŁADNIE w momencie formowania odpowiedzi,
        // zabezpieczając nas przed wyjątkiem "Headers are read-only".
        context.Response.OnStarting(() =>
        {
            var contentType = context.Response.ContentType ?? string.Empty;
            
            // Przykład warunkowego dodania nagłówka zabezpieczającego
            if (contentType.Contains("text/html", StringComparison.OrdinalIgnoreCase))
            {
                context.Response.Headers.Append("X-Performance-Tracking", "Enabled");
            }
            
            return Task.CompletedTask;
        });

        // Przekazanie sterowania dalej do rurociągu (np. Endpointów API lub Blazora)
        await next(context);

        stopwatch.Stop();

        // Po powrocie z 'next' możemy bezpiecznie czytać parametry READ-ONLY do logów,
        // jednak nie wolno nam modyfikować już obiektu Response,
        // a same własności mogły ulec strumieniowaniu. 
        if (!context.Response.HasStarted) 
        {
             // Fallback dla specyficznych scenariuszy, w których odpowiedź jest wstrzymana
        }
        
        _logger.LogInformation(
            "Przetwarzanie ścieżki {Path} zakończono w {Elapsed} ms. Kod statusu: {StatusCode}",
            context.Request.Path,
            stopwatch.ElapsedMilliseconds,
            context.Response.StatusCode);
    }
}
```

```csharp
// Program.cs - Rejestracja
var builder = WebApplication.CreateBuilder(args);

// Middleware oparty o interfejs IMiddleware MUSI być zarejestrowany w kontenerze DI
builder.Services.AddScoped<PerformanceTrackingMiddleware>();

var app = builder.Build();

app.UseHttpsRedirection();

// Dodanie Middleware do rurociągu żądań serwera Kestrel
app.UseMiddleware<PerformanceTrackingMiddleware>();

app.MapRazorComponents<App>()
    .AddInteractiveServerRenderMode();

app.Run();
```

## Wnioski architektoniczne

Porzucenie modelu stanu powiązanego z cyklem życia okna (Desktop) lub scentralizowanych modułów IIS na rzecz rurociągu Middleware w .NET 10 wymaga precyzyjnego zarządzania zależnościami. Implementacja logiki współdzielonej poprzez interfejs `IMiddleware` chroni inżynierów przed powstawaniem trudnych do wykrycia błędów związanych z *Captive Dependencies* w konstruktorach. Ponadto, asynchroniczna natura serwera Kestrel wymusza traktowanie obiektu odpowiedzi HTTP jako strumienia, do którego metadane (takie jak typ zawartości) należy dodawać wyłącznie z użyciem zdarzenia `OnStarting`, co trwale zapobiega naruszaniu specyfikacji protokołu HTTP i awariom potoku wykonawczego.