ASP.NET Core Middleware Pipeline: Ein tiefer Einblick

11 Min. Lesezeit9. März 2026
ASP.NET Core middleware.NET middleware pipelineCustom middleware C#ProblemDetails .NETRate limiting .NETException handling middlewareMiddleware ordering.NET request pipeline

# ASP.NET Core Middleware im Detail

Middleware bildet das Rueckgrat jeder ASP.NET Core Anwendung. Jeder HTTP-Request, der Ihre Anwendung erreicht, durchlaeuft eine Kette von Middleware-Komponenten, bevor er den Endpunkt erreicht, und die Antwort wandert in umgekehrter Reihenfolge durch dieselbe Kette zurueck. Dieses Konzept zu verstehen ist keine Kuer — es bestimmt unmittelbar, wie Ihre Anwendung Authentifizierung, Logging, Fehlerbehandlung und zahlreiche andere Querschnittsaspekte verarbeitet.

In Produktions-APIs, die ich gebaut habe, war die Reihenfolge der Middleware die Ursache fuer einige der verwirrendsten Fehler. Eine Authentifizierungs-Middleware vor CORS fuehrt dazu, dass Preflight-Requests mit 401 abgelehnt werden. Ein Exception-Handler, der nach der Routing-Middleware registriert wird, verpasst Ausnahmen waehrend der Routenaufloesung. Diese Probleme tauchen in Unit-Tests nicht auf und zeigen sich erst unter realer Last.

Wie die Middleware-Pipeline funktioniert

Die ASP.NET Core Middleware-Pipeline ist eine Reihe von Delegates, die miteinander verkettet sind. Jede Middleware-Komponente erhaelt den `HttpContext` und eine Referenz auf die naechste Middleware in der Kette. Sie kann vor dem Aufruf des naechsten Delegates arbeiten, danach, oder beides. Sie kann auch entscheiden, den naechsten Delegate gar nicht aufzurufen — das nennt man Short-Circuiting.

csharp
class=class="code-string">"code-comment">// Konzeptionelles Modell der Pipeline
class=class="code-string">"code-comment">// Request fliesst HINEIN: Middleware class="code-number">1 → class="code-number">2 → class="code-number">3 → Endpunkt
class=class="code-string">"code-comment">// Response fliesst HERAUS: Middleware class="code-number">3 → class="code-number">2 → class="code-number">1 → Client

app.Use(async (context, next) =>
{
    class=class="code-string">"code-comment">// class="code-number">1. Vorverarbeitung: laeuft auf dem Hinweg
    Console.WriteLine(class="code-string">"Middleware class="code-number">1: Vorher");

    await next(context); class=class="code-string">"code-comment">// Kontrolle an die naechste Middleware uebergeben

    class=class="code-string">"code-comment">// class="code-number">2. Nachverarbeitung: laeuft auf dem Rueckweg
    Console.WriteLine(class="code-string">"Middleware class="code-number">1: Nachher");
});

Stellen Sie sich das wie ineinander verschachtelte russische Puppen vor. Die aeusserste Middleware umschliesst alles, und jede nachfolgende Middleware umschliesst die darauf folgenden. Der Request tritt von aussen ein, passiert jede Schicht, trifft im Zentrum auf den Endpunkt, und die Antwort wird dann in umgekehrter Reihenfolge durch jede Schicht zurueckgeleitet.

Dieses Design bietet Ihnen zwei Eingriffspunkte pro Middleware: einen auf dem Hinweg (vor `await next()`) und einen auf dem Rueckweg (nach `await next()`). Eine Logging-Middleware kann beispielsweise die Startzeit vor dem Aufruf von `next` erfassen und die verstrichene Dauer danach berechnen.

Reihenfolge der integrierten Middleware

Die Reihenfolge, in der Sie Middleware in `Program.cs` registrieren, definiert direkt die Ausfuehrungsreihenfolge. Fehler hier fuehren zu Problemen, die notorisch schwer zu diagnostizieren sind.

Hier ist die empfohlene Reihenfolge fuer eine typische Produktions-API:

csharp
var builder = WebApplication.CreateBuilder(args);

class=class="code-string">"code-comment">// Service-Registrierung...

var app = builder.Build();

class=class="code-string">"code-comment">// class="code-number">1. Fehlerbehandlung — ganz aussen, faengt alles ab
app.UseExceptionHandler();
app.UseStatusCodePages();

class=class="code-string">"code-comment">// class="code-number">2. HSTS und HTTPS-Umleitung
if (!app.Environment.IsDevelopment())
{
    app.UseHsts();
}
app.UseHttpsRedirection();

class=class="code-string">"code-comment">// class="code-number">3. Statische Dateien (vor Routing, damit Assets die Auth umgehen)
app.UseStaticFiles();

class=class="code-string">"code-comment">// class="code-number">4. Routing — bestimmt den gematchten Endpunkt
app.UseRouting();

class=class="code-string">"code-comment">// class="code-number">5. CORS — nach Routing und vor Auth
app.UseCors();

class=class="code-string">"code-comment">// class="code-number">6. Authentifizierung — wer sind Sie?
app.UseAuthentication();

class=class="code-string">"code-comment">// class="code-number">7. Autorisierung — duerfen Sie das?
app.UseAuthorization();

class=class="code-string">"code-comment">// class="code-number">8. Rate Limiting — nach Auth fuer benutzerbezogene Limits
app.UseRateLimiter();

class=class="code-string">"code-comment">// class="code-number">9. Response-Caching und -Komprimierung
app.UseResponseCaching();
app.UseResponseCompression();

class=class="code-string">"code-comment">// class="code-number">10. Eigene Middleware

class=class="code-string">"code-comment">// class="code-number">11. Endpunkt-Ausfuehrung
app.MapControllers();

app.Run();

Warum ist die Reihenfolge so wichtig? `UseExceptionHandler` muss zuerst kommen, um Ausnahmen aller nachfolgenden Middleware abzufangen. `UseRouting` muss vor `UseAuthentication` stehen, damit die Authentifizierungs-Middleware weiss, welcher Endpunkt gematcht wurde, und die richtige Auth-Richtlinie anwenden kann. `UseCors` muss zwischen Routing und Autorisierung stehen — wenn CORS vor dem Routing laeuft, kann es die CORS-Richtlinie des gematchten Endpunkts nicht bestimmen, und wenn es nach der Autorisierung laeuft, werden Preflight-Requests mit 401 abgelehnt.

Eigene Middleware schreiben

Inline-Middleware mit `app.Use`

Der schnellste Weg, Middleware hinzuzufuegen, ist inline mit einem Lambda. Das eignet sich fuer Prototyping und einfache Szenarien:

csharp
app.Use(async (context, next) =>
{
    var correlationId = context.Request.Headers[class="code-string">"X-Correlation-Id"].FirstOrDefault()
        ?? Guid.NewGuid().ToString();

    context.Response.Headers[class="code-string">"X-Correlation-Id"] = correlationId;
    context.Items[class="code-string">"CorrelationId"] = correlationId;

    await next(context);
});

Klassenbasierte Middleware

Fuer alles, was ueber triviale Logik hinausgeht, sollte die Middleware in eine eigene Klasse ausgelagert werden. ASP.NET Core Middleware folgt einer Konvention: ein Konstruktor, der `RequestDelegate` akzeptiert, und eine `InvokeAsync`-Methode, die `HttpContext` entgegennimmt:

csharp
public class CorrelationIdMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<CorrelationIdMiddleware> _logger;

    public CorrelationIdMiddleware(RequestDelegate next, ILogger<CorrelationIdMiddleware> logger)
    {
        _next = next;
        _logger = logger;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        var correlationId = context.Request.Headers[class="code-string">"X-Correlation-Id"].FirstOrDefault()
            ?? Guid.NewGuid().ToString();

        context.Items[class="code-string">"CorrelationId"] = correlationId;
        context.Response.OnStarting(() =>
        {
            context.Response.Headers[class="code-string">"X-Correlation-Id"] = correlationId;
            return Task.CompletedTask;
        });

        using (_logger.BeginScope(new Dictionary<string, object>
        {
            [class="code-string">"CorrelationId"] = correlationId
        }))
        {
            await _next(context);
        }
    }
}

class=class="code-string">"code-comment">// Registrierung in Program.cs
app.UseMiddleware<CorrelationIdMiddleware>();

Beachten Sie ein subtiles Detail: Ich verwende `context.Response.OnStarting` statt den Header direkt vor dem Aufruf von `next` zu setzen. Das stellt sicher, dass der Header erst unmittelbar vor dem Senden der Response-Header gesetzt wird, was Probleme vermeidet, falls eine spaetere Middleware Header aendert.

Konventionsbasierte Erweiterungsmethoden

Produktionsreife Middleware sollte eine Erweiterungsmethode fuer saubere Registrierung bereitstellen:

csharp
public static class MiddlewareExtensions
{
    public static IApplicationBuilder UseCorrelationId(this IApplicationBuilder app)
    {
        return app.UseMiddleware<CorrelationIdMiddleware>();
    }

    public static IApplicationBuilder UseRequestTiming(this IApplicationBuilder app)
    {
        return app.UseMiddleware<RequestTimingMiddleware>();
    }
}

class=class="code-string">"code-comment">// Saubere Registrierung
app.UseCorrelationId();
app.UseRequestTiming();

Dependency Injection in Middleware

Middleware-Klassen werden beim Anwendungsstart einmalig instanziiert und sind damit effektiv Singletons. Das hat eine kritische Konsequenz: Sie koennen keine Scoped- oder Transient-Services ueber den Konstruktor injizieren.

csharp
class=class="code-string">"code-comment">// FALSCH — DbContext ist Scoped, Middleware ist Singleton
public class SchlechteMiddleware
{
    private readonly RequestDelegate _next;
    private readonly AppDbContext _db; class=class="code-string">"code-comment">// Gefangene Abhaengigkeit!

    public SchlechteMiddleware(RequestDelegate next, AppDbContext db)
    {
        _next = next;
        _db = db; class=class="code-string">"code-comment">// Dieselbe Instanz fuer jeden Request
    }
}

class=class="code-string">"code-comment">// RICHTIG — Scoped-Services ueber InvokeAsync injizieren
public class GuteMiddleware
{
    private readonly RequestDelegate _next;

    public GuteMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task InvokeAsync(HttpContext context, AppDbContext db, IUserService userService)
    {
        class=class="code-string">"code-comment">// db und userService werden pro Request aus dem DI-Container aufgeloest
        var user = await userService.GetCurrentUserAsync(context.User);
        await _next(context);
    }
}

Die `InvokeAsync`-Methode unterstuetzt Method Injection — ASP.NET Core loest ihre Parameter fuer jeden Request aus dem DI-Container auf. Verwenden Sie den Konstruktor nur fuer Singleton-Abhaengigkeiten (`RequestDelegate`, `ILogger`, `IOptions` usw.) und `InvokeAsync`-Parameter fuer Scoped- und Transient-Abhaengigkeiten.

Fehlerbehandlungs-Middleware

Integrierte ProblemDetails (RFC 7807)

.NET 7+ bietet eine erstklassige ProblemDetails-Integration, die Fehlerantworten gemaess RFC 7807 standardisiert. Diesen Ansatz verwende ich in jeder Produktions-API:

csharp
builder.Services.AddProblemDetails(options =>
{
    options.CustomizeProblemDetails = context =>
    {
        context.ProblemDetails.Extensions[class="code-string">"traceId"] = context.HttpContext.TraceIdentifier;
        context.ProblemDetails.Extensions[class="code-string">"instance"] = context.HttpContext.Request.Path;
    };
});

app.UseExceptionHandler();
app.UseStatusCodePages();

Eigene Fehlerbehandlungs-Middleware

Fuer mehr Kontrolle schreiben Sie einen dedizierten Exception-Handler, der Domain-Ausnahmen auf passende HTTP-Antworten abbildet:

csharp
public class GlobalExceptionMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<GlobalExceptionMiddleware> _logger;

    public GlobalExceptionMiddleware(RequestDelegate next, ILogger<GlobalExceptionMiddleware> logger)
    {
        _next = next;
        _logger = logger;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        try
        {
            await _next(context);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, class="code-string">"Unbehandelte Ausnahme bei {Method} {Path}",
                context.Request.Method, context.Request.Path);

            await HandleExceptionAsync(context, ex);
        }
    }

    private static async Task HandleExceptionAsync(HttpContext context, Exception exception)
    {
        var (statusCode, title) = exception switch
        {
            ArgumentValidationException => (StatusCodes.Status400BadRequest, class="code-string">"Validierungsfehler"),
            NotFoundException => (StatusCodes.Status404NotFound, class="code-string">"Ressource nicht gefunden"),
            ConflictException => (StatusCodes.Status409Conflict, class="code-string">"Konflikt"),
            UnauthorizedAccessException => (StatusCodes.Status403Forbidden, class="code-string">"Zugriff verweigert"),
            _ => (StatusCodes.Status500InternalServerError, class="code-string">"Interner Serverfehler")
        };

        context.Response.StatusCode = statusCode;
        context.Response.ContentType = class="code-string">"application/problem+json";

        var problemDetails = new ProblemDetails
        {
            Status = statusCode,
            Title = title,
            Detail = statusCode == class="code-number">500 ? class="code-string">"Ein unerwarteter Fehler ist aufgetreten." : exception.Message,
            Type = $class="code-string">"https:class="code-commentclass="code-string">">//httpstatuses.com/{statusCode}",
            Extensions =
            {
                [class="code-string">"traceId"] = context.TraceIdentifier
            }
        };

        await context.Response.WriteAsJsonAsync(problemDetails);
    }
}

Request/Response-Logging-Middleware

Das Protokollieren jedes Requests und jeder Response ist fuer Debugging und Auditing unschaetzbar wertvoll. Aber Vorsicht — das Loggen von Request- und Response-Bodies kann sensible Daten preisgeben und enorme Speichermengen verbrauchen.

csharp
public class RequestLoggingMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<RequestLoggingMiddleware> _logger;

    public RequestLoggingMiddleware(RequestDelegate next, ILogger<RequestLoggingMiddleware> logger)
    {
        _next = next;
        _logger = logger;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        var stopwatch = Stopwatch.StartNew();
        var requestPath = context.Request.Path;
        var method = context.Request.Method;

        _logger.LogInformation(class="code-string">"Request gestartet: {Method} {Path} {QueryString}",
            method, requestPath, context.Request.QueryString);

        try
        {
            await _next(context);
            stopwatch.Stop();

            _logger.LogInformation(
                class="code-string">"Request abgeschlossen: {Method} {Path} — {StatusCode} — {DauerMs}ms",
                method, requestPath, context.Response.StatusCode, stopwatch.ElapsedMilliseconds);
        }
        catch (Exception ex)
        {
            stopwatch.Stop();
            _logger.LogError(ex,
                class="code-string">"Request fehlgeschlagen: {Method} {Path} — Ausnahme nach {DauerMs}ms",
                method, requestPath, stopwatch.ElapsedMilliseconds);
            throw;
        }
    }
}

In Produktionssystemen filtere ich immer Health-Check-Endpunkte und Anfragen fuer statische Dateien heraus. Ohne Filterung ertrinkt Ihre Logging-Infrastruktur im Rauschen von Kubernetes-Probes, die alle paar Sekunden `/health` abfragen.

Rate-Limiting-Middleware

.NET 7 hat eingebautes Rate Limiting eingefuehrt. Hier ist ein produktionsreifes Setup mit mehreren Richtlinien:

csharp
builder.Services.AddRateLimiter(options =>
{
    options.RejectionStatusCode = StatusCodes.Status429TooManyRequests;

    class=class="code-string">"code-comment">// Globale Fixed-Window-Richtlinie
    options.AddFixedWindowLimiter(class="code-string">"fixed", opt =>
    {
        opt.PermitLimit = class="code-number">100;
        opt.Window = TimeSpan.FromMinutes(class="code-number">1);
        opt.QueueProcessingOrder = QueueProcessingOrder.OldestFirst;
        opt.QueueLimit = class="code-number">10;
    });

    class=class="code-string">"code-comment">// Sliding-Window-Richtlinie pro Benutzer
    options.AddSlidingWindowLimiter(class="code-string">"per-user", opt =>
    {
        opt.PermitLimit = class="code-number">30;
        opt.Window = TimeSpan.FromMinutes(class="code-number">1);
        opt.SegmentsPerWindow = class="code-number">6;
    });

    class=class="code-string">"code-comment">// Token-Bucket fuer API-Schluessel
    options.AddTokenBucketLimiter(class="code-string">"api-key", opt =>
    {
        opt.TokenLimit = class="code-number">200;
        opt.ReplenishmentPeriod = TimeSpan.FromSeconds(class="code-number">10);
        opt.TokensPerPeriod = class="code-number">20;
    });

    options.OnRejected = async (context, cancellationToken) =>
    {
        context.HttpContext.Response.ContentType = class="code-string">"application/problem+json";
        var problem = new ProblemDetails
        {
            Status = class="code-number">429,
            Title = class="code-string">"Zu viele Anfragen",
            Detail = class="code-string">"Rate Limit ueberschritten. Bitte versuchen Sie es nach dem Zuruecksetzen erneut."
        };
        await context.HttpContext.Response.WriteAsJsonAsync(problem, cancellationToken);
    };
});

app.UseRateLimiter();

Richtlinien auf bestimmte Endpunkte anwenden:

csharp
app.MapGet(class="code-string">"/api/products", GetProducts)
    .RequireRateLimiting(class="code-string">"per-user");

app.MapPost(class="code-string">"/api/orders", CreateOrder)
    .RequireRateLimiting(class="code-string">"fixed");

Middleware vs Endpoint-Filter vs Action-Filter

Eine der haeufigsten Fragen, die ich bekomme, ist, wann Middleware und wann Filter verwendet werden sollte. Hier ist die Abgrenzung:

Middleware arbeitet auf jedem Request, der die Pipeline durchlaeuft. Sie kennt keine MVC-Konzepte wie Controller, Actions oder Model Binding. Verwenden Sie sie fuer globale Querschnittsaspekte: Logging, Correlation IDs, Fehlerbehandlung, CORS.

Action-Filter sind ein MVC-Konzept. Sie laufen nach dem Model Binding und haben Zugriff auf `ActionExecutingContext`, der Action-Argumente, Controller-Instanz und Model State umfasst. Verwenden Sie sie fuer controllerspezifische Belange: Auditing welche Action aufgerufen wurde, Model State validieren, Action-Ergebnisse transformieren.

Endpoint-Filter (eingefuehrt in .NET 7) sind das Minimal-API-Gegenstueck zu Action-Filtern. Sie umschliessen einzelne Endpoint-Handler und haben Zugriff auf die Argumente des Endpunkts:

csharp
app.MapPost(class="code-string">"/api/products", CreateProduct)
    .AddEndpointFilter(async (context, next) =>
    {
        var request = context.GetArgument<CreateProductRequest>(class="code-number">0);
        if (string.IsNullOrWhiteSpace(request.Name))
        {
            return Results.ValidationProblem(
                new Dictionary<string, string[]>
                {
                    [class="code-string">"Name"] = [class="code-string">"Produktname ist erforderlich."]
                });
        }
        return await next(context);
    });

| Aspekt | Middleware | Action-Filter | Endpoint-Filter |

|--------|-----------|---------------|-----------------|

| Geltungsbereich | Jeder Request | Nur MVC-Actions | Minimal-API-Endpunkte |

| Zugriff auf Action-Kontext | Nein | Ja | Ja (Argumente) |

| Laeuft vor Model Binding | Ja | Nein | Nein |

| Kann kurzschliessen | Ja | Ja | Ja |

| DI-Unterstuetzung | InvokeAsync-Parameter | Konstruktor | Konstruktor/Lambda |

| Am besten fuer | Globale Querschnittsaspekte | MVC-spezifische Logik | Minimal-API-Validierung |

Haeufige Middleware-Fehler

1. Falsche Reihenfolge

Der schaedlichste Fehler. `UseAuthentication` vor `UseRouting` zu platzieren bedeutet, dass die Auth-Middleware nicht weiss, welcher Endpunkt gematcht wurde, sodass endpunktspezifische `[Authorize]`-Richtlinien nicht korrekt funktionieren. `UseExceptionHandler` nach Ihrer eigenen Middleware zu platzieren bedeutet, dass Ausnahmen aus dieser Middleware nicht behandelt werden.

2. `next()` vergessen aufzurufen

Wenn Ihre Middleware bedingt `await next()` ueberspringt, ohne eine Antwort zu schreiben, erhaelt der Client eine leere 200-Antwort. Fuer Short-Circuiting (z.B. eine gecachte Antwort zurueckgeben) ist das eine legitime Technik, aber versehentliches Vergessen des Aufrufs erzeugt verwirrende Fehler.

csharp
class=class="code-string">"code-comment">// Fehler: next im else-Zweig vergessen
app.Use(async (context, next) =>
{
    if (context.Request.Headers.ContainsKey(class="code-string">"X-Block"))
    {
        context.Response.StatusCode = class="code-number">403;
        await context.Response.WriteAsync(class="code-string">"Blockiert");
        return; class=class="code-string">"code-comment">// Short-Circuit — beabsichtigt
    }
    class=class="code-string">"code-comment">// FEHLER: Wenn wir hierher gelangen und await next(context) vergessen,
    class=class="code-string">"code-comment">// gibt der Request stillschweigend eine leere class="code-number">200-Antwort zurueck
    await next(context); class=class="code-string">"code-comment">// Das darf nicht fehlen!
});

3. Response aendern, nachdem sie gestartet wurde

Sobald `context.Response.HasStarted` true zurueckgibt, koennen Sie Header oder Statuscode nicht mehr aendern. Das passiert, wenn eine spaetere Middleware oder der Endpunkt bereits begonnen hat, den Response-Body zu schreiben:

csharp
app.Use(async (context, next) =>
{
    await next(context);

    class=class="code-string">"code-comment">// Das wirft eine Ausnahme, wenn die Response bereits gestreamt wird
    if (!context.Response.HasStarted)
    {
        context.Response.Headers[class="code-string">"X-Custom-Header"] = class="code-string">"wert";
    }
});

4. Schwere Arbeit in Middleware

Middleware laeuft bei jedem Request. Datenbankabfragen, externe API-Aufrufe oder komplexe Berechnungen in Middleware vernichten Ihren Durchsatz. Wenn Sie Request-bezogene Datenanreicherung benoetigen, erwaegen Sie das Cachen der Ergebnisse oder verlagern Sie die Logik in einen Action-Filter, der nur fuer relevante Endpunkte ausgefuehrt wird.

5. Ausnahmen nicht erneut werfen

Eine Ausnahme in Middleware fangen und nicht erneut werfen verschluckt den Fehler stillschweigend. Sofern Sie nicht bewusst einen Exception-Handler schreiben, werfen Sie immer erneut:

csharp
class=class="code-string">"code-comment">// FALSCH — verschluckt die Ausnahme, keine andere Middleware sieht sie
app.Use(async (context, next) =>
{
    try { await next(context); }
    catch (Exception ex)
    {
        _logger.LogError(ex, class="code-string">"Etwas ist schiefgelaufen");
        class=class="code-string">"code-comment">// Fehlt: throw; oder Fehlerantwort schreiben
    }
});

Bedingte Middleware-Ausfuehrung

Sie koennen die Middleware-Pipeline basierend auf Request-Eigenschaften verzweigen:

csharp
class=class="code-string">"code-comment">// Nur auf API-Routen anwenden
app.UseWhen(
    context => context.Request.Path.StartsWithSegments(class="code-string">"/api"),
    appBuilder =>
    {
        appBuilder.UseMiddleware<ApiKeyValidationMiddleware>();
        appBuilder.UseMiddleware<RequestLoggingMiddleware>();
    });

class=class="code-string">"code-comment">// Separate Pipeline fuer Health Checks
app.Map(class="code-string">"/health", appBuilder =>
{
    appBuilder.Run(async context =>
    {
        context.Response.StatusCode = class="code-number">200;
        await context.Response.WriteAsync(class="code-string">"Gesund");
    });
});

`UseWhen` fuegt sich nach dem Zweig wieder in die Hauptpipeline ein, waehrend `Map` einen terminalen Zweig erzeugt, der nicht zurueckkehrt.

Terminale Middleware

`app.Run` registriert terminale Middleware — sie erhaelt keinen `next`-Delegate und beendet die Pipeline immer:

csharp
class=class="code-string">"code-comment">// Fallback fuer nicht gematchte Routen
app.Run(async context =>
{
    context.Response.StatusCode = class="code-number">404;
    context.Response.ContentType = class="code-string">"application/problem+json";
    await context.Response.WriteAsJsonAsync(new ProblemDetails
    {
        Status = class="code-number">404,
        Title = class="code-string">"Nicht gefunden",
        Detail = $class="code-string">"Kein Endpunkt passt zum Pfad class="code-string">'{context.Request.Path}'."
    });
});

Fazit

Die Middleware-Pipeline ist der Ort, an dem das Querschnittsverhalten Ihrer Anwendung lebt. Die richtige Reihenfolge einzuhalten, den Singleton-Lebenszyklus zu verstehen und zu wissen, wann Middleware statt Filter eingesetzt werden sollte, trennt robuste APIs von fragilen. Jeder Produktionsvorfall, den ich im Zusammenhang mit Middleware untersucht habe, lief auf einen der oben genannten Fehler hinaus — Reihenfolge, fehlende `next()`-Aufrufe oder versehentliches Einfangen von Scoped-Services als Singletons. Beherrschen Sie diese Grundlagen, und die Pipeline wird zu einem Ihrer staerksten architektonischen Werkzeuge.

Gerne unterstuetze ich Sie bei der Gestaltung und Ueberpruefung Ihrer Middleware-Pipeline fuer den Produktionseinsatz.

Verwandte Artikel

Haben Sie ein Flutter-Projekt?

Ich entwickle hochleistungsfähige Flutter-Anwendungen für iOS, Android und Web.

Kontakt aufnehmen