ASP.NET Core Middleware

Hinweis

Dies ist nicht die neueste Version dieses Artikels. Die aktuelle Version finden Sie in der .NET 10-Version dieses Artikels.

Warnung

Diese Version von ASP.NET Core wird nicht mehr unterstützt. Weitere Informationen finden Sie in der .NET- und .NET Core-Supportrichtlinie. Die aktuelle Version finden Sie in der .NET 10-Version dieses Artikels.

Middleware ist Software, die zu einer Anwendungspipeline zusammengesetzt wird, um Anforderungen und Antworten zu verarbeiten. Jede Middleware:

  • Wählt aus, ob die Anforderung an die nächste Middleware in der Pipeline übergeben werden soll.
  • Kann Arbeiten ausführen, bevor oder nachdem die nächste Middleware in der Pipeline aufgerufen wird.

Anforderungsdelegaten werden verwendet, um die Anforderungspipeline zu erstellen. Die Anforderungsdelegaten behandeln jede HTTP-Anforderung.

Konfigurieren Sie Anforderungsdelegaten mithilfe der Erweiterungsmethoden Run, Map und Use. Sie können einen einzelnen Anforderungsdelegat inline als anonyme Methode (als Inline-Middleware bezeichnet) angeben oder in einer wiederverwendbaren Klasse definieren. Diese inline anonymen Methoden oder wiederverwendbaren Klassen werden als Middleware - oder Middleware-Komponenten bezeichnet. Jede Middleware in der Anfragepipeline ist dafür verantwortlich, die nächste Middleware in der Pipeline aufzurufen oder die Pipeline kurzzuschließen. Wenn eine Middleware einen Kurzschluss verursacht, wird diese als Terminalmiddleware bezeichnet, da sie verhindert, dass weitere Middleware die Anforderung verarbeiten kann.

Weitere Informationen zum Unterschied zwischen Anforderungspipelines in ASP.NET Core und ASP.NET 4.x mit zusätzlichen Middlewarebeispielen finden Sie unter Migrieren von HTTP-Modulen zu ASP.NET Core Middleware.

Rolle von Middleware nach App-Typ

Serverseitige Blazor und Razor Seiten sowie MVC verarbeiten Browseranforderungen auf dem Server mit Middleware. Die Anleitungen in diesem Artikel gelten für diese Arten von Apps.

Unabhängige Blazor WebAssembly-Apps werden vollständig auf dem Client ausgeführt und verarbeiten keine Anforderungen mit einer Middleware-Pipeline. Die Anleitung in diesem Artikel gilt nicht für eigenständige Blazor WebAssembly-Apps.

Middleware-Codeanalyse

Weitere Informationen zu den Compilerplattformanalysatoren von ASP.NET Core, die App-Code auf Qualität prüfen, finden Sie unter Diagnose-Code Analysis in ASP.NET Core Apps.

Erstellen einer Middlewarepipeline mit WebApplication

Die ASP.NET Core-Anforderungspipeline besteht aus einer Sequenz von Anforderungsdelegaten, die nacheinander aufgerufen werden. Das Konzept wird im folgenden Diagramm veranschaulicht. Der Ausführungsthread folgt den schwarzen Pfeilen.

Anforderungsverarbeitungsmuster, das zeigt, wie eine Anforderung eingeht, drei Middlewarekomponenten durchläuft und die Antwort die App verlässt. Jede Middleware führt ihre Logik aus und übergibt die Anforderung mit der Anweisung next() an die nächste Middlewarekomponente. Nachdem die dritte Middlewarekomponente die Anforderung verarbeitet hat, durchläuft die Anforderung wieder die beiden vorherigen Middlewarekomponenten in umgekehrter Reihenfolge zur weiteren Verarbeitung nach deren next()-Anweisungen, bevor sie die App als Antwort an den Client verlässt.

Jeder Delegat kann Vorgänge vor und nach dem nächsten Delegaten ausführen. Die Ausnahmebehandlungsdelegaten sollen früh in der Pipeline aufgerufen werden, sodass sie Ausnahmen abfangen können, die in späteren Stadien der Pipeline auftreten.

Hinweis

Um lokal mit den Codebeispielen in diesem Abschnitt zu experimentieren, erstellen Sie eine ASP.NET Core-App mit der ASP.NET Core Empty-Projektvorlage . Bei Verwendung der .NET CLI lautet web der Kurzname der Vorlage (dotnet new web).

Die einfachste ASP.NET Core-App ruft Run auf, um eine einzelne Terminal-Middleware als anonymen Funktions-Request-Delegaten einzurichten, der Anfragen ohne eine Anforderungspipeline verarbeitet.

Im folgenden Beispiel:

  • Der Aufruf von RunExtensions.Run wird bei jeder Anforderung ausgeführt und schreibt "Hello world!" in die Antwort.
  • Der Aufruf von WebApplication.Run am Ende des Codeblocks führt die App aus und blockiert den aufrufenden Thread bis zum Herunterfahren des Hosts.
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.Run(async context =>
{
    await context.Response.WriteAsync("Hello world!");
});

app.Run();

Antwort beim Zugriff auf die App in einem Browser unter der Start-URL:

Hello world!

Mit Use können Sie mehrere Anforderungsdelegaten miteinander verknüpfen. Der Parameter next steht für den nächsten Delegaten in der Pipeline. In der Regel können Sie Aktionen vor und nach dem next-Delegat ausführen.

Dies wird im folgenden Beispiel veranschaulicht:

  • Zwei Use Aufrufe, die jeweils in die Konsole geschrieben werden:
    • Wo Aufgaben ausgeführt werden können, kann die Antwort (context.Response, HttpResponse) geschrieben werden.
    • Wo Aufgaben durchgeführt werden, die keine Daten an die Antwort senden, nachdem der Parameter next aufgerufen wurde.
  • Ein Terminal-Anforderungsdelegat mit einem Aufruf von RunExtensions.Run, das "Hello world!" in die Antwort schreibt.
  • Ein letzter Use Aufruf, der nie ausgeführt wird, da er dem Run Terminalanforderungsdelegat folgt.
  • Ein Aufruf von WebApplication.Run am Ende des Codeblocks, um die App auszuführen und den aufrufenden Thread bis zum Herunterfahren des Hosts zu blockieren.
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.Use(async (context, next) =>
{
    Console.WriteLine("Work that can write to the response. (1)");
    await next.Invoke(context);
    Console.WriteLine("Work that doesn't write to the response. (1)");
});

app.Use(async (context, next) =>
{
    Console.WriteLine("Work that can write to the response. (2)");
    await next.Invoke(context);
    Console.WriteLine("Work that doesn't write to the response. (2)");
});

app.Run(async context =>
{
    await context.Response.WriteAsync("Hello world!");
});

app.Use(async (context, next) =>
{
    Console.WriteLine("This statement isn't reached. (3)");
    await next.Invoke(context);
    Console.WriteLine("This statement isn't reached. (3)");
});

app.Run();

Im Konsolenfenster der App, wenn die App ausgeführt wird:

Aufgaben, die Daten in die Antwort schreiben können. (1)
Aufgaben, die Daten in die Antwort schreiben können. (2)
Aufgaben, die keine Daten in die Antwort schreiben. (2)
Aufgaben, die keine Daten in die Antwort schreiben. (1)

Die Kurzschaltung der Anforderungspipeline ist häufig wünschenswert, da unnötige Arbeit vermieden wird. So kann beispielsweise Middleware für statische Dateien als terminale Middleware fungieren, indem sie eine Anforderung für eine statische Datei verarbeitet und den Rest der Pipeline umgeht. Middleware, die vor der Terminal-Middleware in die Pipeline eingefügt wurde, verarbeitet den Code nach deren next.Invoke-Anweisungen weiterhin. Wenn Sie nicht beabsichtigen, next.Invoke aufzurufen, da Ihr Ziel darin besteht, die Pipeline zu beenden, verwenden Sie stattdessen einen Run Delegaten anstatt die Use Erweiterungsmethode aufzurufen.

Rufen Sie next.Invoke während oder nach dem Senden der Antwort an den Client nicht auf. Nachdem ein HttpResponse Vorgang gestartet wurde, führen Änderungen zu einer Ausnahme. Das Festlegen von Headern oder ein Antwortstatuscode löst beispielsweise eine Ausnahme aus, nachdem die Antwort gestartet wurde. Wenn Sie nach dem Aufruf von next in den Antworttext schreiben, kann dies zu Folgendem führen:

  • Es wird eine Protokollverletzung verursacht. Es werden beispielsweise mehr Bytes in die Antwort geschrieben als in der zulässigen Inhaltslänge des Antwort-Headers spezifiziert ist (Content-Length Headerwert).
  • Der Textkörper kann beschädigt werden; es wird z. B. eine HTML-Fußzeile in eine CSS-Datei geschrieben.

Um festzustellen, ob die Antwort gestartet wurde, überprüfen Sie den Wert von HasStarted.

Weitere Informationen finden Sie unter Kurzschluss der Middleware nach dem Routing.

Run-Delegat

Ein Run-Delegat erhält keinen next-Parameter. Der erste Run Delegat beendet immer die Pipeline. Run ist ebenfalls eine Konvention, und manche Middleware kann Run-Methoden offenlegen, die am Ende der Pipeline ausgeführt werden.

Alle Use oder Run-Delegaten nach dem ersten Run-Delegaten werden nicht aufgerufen.

Verzweigen der Middleware-Pipeline

Map Erweiterungen werden als Konvention verwendet, um die Anforderungsverarbeitungspipeline zu verzweigen. Map brancht die Anforderungspipeline auf Grundlage von Übereinstimmungen des angegebenen Anforderungspfads. Wenn der Anfragepfad mit dem angegebenen Pfad beginnt, wird der Zweig ausgeführt.

Im folgenden Beispiel wird HandleMap1 für Anforderungen an /map1 aufgerufen, und HandleMap2 wird für Anforderungen an /map2 aufgerufen.

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.Map("/map1", HandleMap1);
app.Map("/map2", HandleMap2);

app.Run(async context =>
{
    await context.Response.WriteAsync("Hello from the non-Map delegate!");
});

app.Run();

private static void HandleMap1(IApplicationBuilder app)
{
    app.Run(async context =>
    {
        await context.Response.WriteAsync("Map 1");
    });
}

private static void HandleMap2(IApplicationBuilder app)
{
    app.Run(async context =>
    {
        await context.Response.WriteAsync("Map 2");
    });
}

In der folgenden Tabelle sind die Anforderungen und Antworten mit dem vorherigen Code aufgeführt.

Anforderung Antwort
/ Hello from the non-Map delegate.
/map1 Map 1
/map2 Map 2
/map3 Hello from the non-Map delegate.

Bei Verwendung von Map werden die übereinstimmenden Pfadsegmente aus HttpRequest.Path entfernt und für jede Anforderung an HttpRequest.PathBase angehängt.

Map kann mehrere Segmente gleichzeitig abgleichen:

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.Map("/map1/segment1", HandleMultipleSegments);

app.Run(async context =>
{
    await context.Response.WriteAsync("Hello from the non-Map delegate.");
});

app.Run();

private static void HandleMultipleSegments(IApplicationBuilder app)
{
    app.Run(async context =>
    {
        await context.Response.WriteAsync("Processing '/map1/segment1'");
    });
}

In der folgenden Tabelle sind die Anforderungen und Antworten mit dem vorherigen Code aufgeführt.

Anforderung Antwort
/ Hello from the non-Map delegate.
/map1/segment1 Processing '/map1/segment1'

Map unterstützt Nesting:

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.Map("/level1", level1App => {
    level1App.Map("/level2a", level2AApp => {
        level2AApp.Run(async context =>
        {
            await context.Response.WriteAsync("Processing '/level1/level2a'");
        });
    });
    level1App.Map("/level2b", level2BApp => {
        level2BApp.Run(async context =>
        {
            await context.Response.WriteAsync("Processing '/level1/level2b'");
        });
    });
});

app.Run(async context =>
{
    await context.Response.WriteAsync("Hello from the non-Map delegate!");
});

app.Run();

In der folgenden Tabelle sind die Anforderungen und Antworten mit dem vorherigen Code aufgeführt.

Anforderung Antwort
/ Hello from the non-Map delegate.
/level1/level2a Processing '/level1/level2a'
/level1/level2b Processing '/level1/level2b'

MapWhen verzweigt die Anforderungspipeline basierend auf dem Ergebnis des angegebenen Prädikats. Jedes Prädikat vom Typ Func<HttpContext, bool> kann verwendet werden, um Anforderungen einem neuen Branch der Pipeline zuzuordnen. Im folgenden Beispiel wird ein Prädikat verwendet, um das Vorhandensein einer Abfragezeichenfolgenvariable namens "branch" zu erkennen:

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapWhen(context => context.Request.Query.ContainsKey("branch"), HandleBranch);

app.Run(async context =>
{
    await context.Response.WriteAsync("Hello from the non-Map delegate.");
});

app.Run();

private static void HandleBranch(IApplicationBuilder app)
{
    app.Run(async context =>
    {
        var branchVer = context.Request.Query["branch"];
        await context.Response.WriteAsync($"Branch used = '{branchVer}'");
    });
}

In der folgenden Tabelle sind die Anforderungen und Antworten mit dem vorherigen Code aufgeführt.

Anforderung Antwort
/ Hello from the non-Map delegate.
/?branch=main Branch used = 'main'

UseWhen kann die Anforderungspipeline basierend auf dem Ergebnis des angegebenen Prädikats verzweigen. Anders als bei MapWhen wird dieser Branch wieder mit der Hauptpipeline verbunden, wenn er keine Terminal-Middleware enthält:

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.UseWhen(context => context.Request.Query.ContainsKey("branch"),
    appBuilder => HandleBranchAndRejoin(appBuilder));

app.Run(async context =>
{
    await context.Response.WriteAsync("Hello from the non-Map delegate.");
});

app.Run();

void HandleBranchAndRejoin(IApplicationBuilder app)
{
    var logger = app.ApplicationServices.GetRequiredService<ILogger<Program>>(); 

    app.Use(async (context, next) =>
    {
        var branchVer = context.Request.Query["branch"];
        logger.LogInformation("Branch used = {branchVer}", branchVer.ToString());

        Console.WriteLine("Work that can write to the response.");
        await next.Invoke(context);
        Console.WriteLine("Work that doesn't write to the response.");
    });
}

Im vorherigen Beispiel wird eine Antwort von "Hello from the non-Map delegate." für alle Anforderungen geschrieben. Wenn die Anforderung eine Abfragezeichenfolgenvariable namens "branch" enthält, wird der Wert protokolliert, bevor zur Hauptpipeline zurückgekehrt wird.

Erstellen einer Middlewarepipeline mit IApplicationBuilder

Die ASP.NET Core-Anforderungspipeline besteht aus einer Sequenz von Anforderungsdelegaten, die nacheinander aufgerufen werden. Das Konzept wird im folgenden Diagramm veranschaulicht. Der Ausführungsthread folgt den schwarzen Pfeilen.

Anforderungsverarbeitungsmuster, das zeigt, wie eine Anforderung eingeht, drei Middlewarekomponenten durchläuft und die Antwort die App verlässt. Jede Middleware führt ihre Logik aus und übergibt die Anforderung mit der Anweisung next() an die nächste Middlewarekomponente. Nachdem die dritte Middlewarekomponente die Anforderung verarbeitet hat, durchläuft die Anforderung wieder die beiden vorherigen Middlewarekomponenten in umgekehrter Reihenfolge zur weiteren Verarbeitung nach deren next()-Anweisungen, bevor sie die App als Antwort an den Client verlässt.

Jeder Delegat kann Vorgänge vor und nach dem nächsten Delegaten ausführen. Die Ausnahmebehandlungsdelegaten sollen früh in der Pipeline aufgerufen werden, sodass sie Ausnahmen abfangen können, die in späteren Stadien der Pipeline auftreten.

Die einfachste mögliche ASP.NET Core-App enthält einen einzigen Anforderungsdelegaten, der alle Anforderungen verarbeitet. In diesem Fall ist keine tatsächliche Anforderungspipeline vorhanden. Stattdessen wird eine einzelne anonyme Funktion als Antwort auf jede HTTP-Anforderung aufgerufen.

public class Startup
{
    public void Configure(IApplicationBuilder app)
    {
        app.Run(async context =>
        {
            await context.Response.WriteAsync("Hello, World!");
        });
    }
}

Mit Use können Sie mehrere Anforderungsdelegaten miteinander verknüpfen. Der Parameter next steht für den nächsten Delegaten in der Pipeline. Sie können die Pipeline kurzschließen, indem Sie nicht den Parameter next aufrufen. Normalerweise können Sie Aktionen sowohl vor als auch nach dem nächsten Delegaten durchführen. Dies wird in folgendem Beispiel veranschaulicht:

app.Use(async (context, next) =>
{
    // Do work that doesn't write to the Response.
    await next.Invoke();
    // Do logging or other work that doesn't write to the Response.
});

Wenn ein Delegierter eine Anforderung nicht an den nächsten Delegierten übergibt, wird dies als Kurzschluss der Anforderungspipeline bezeichnet. Das Kurzschließen ist oft sinnvoll, da es unnötige Arbeit verhindert. So kann beispielsweise Middleware für statische Dateien als terminale Middleware fungieren, indem sie eine Anforderung für eine statische Datei verarbeitet und den Rest der Pipeline umgeht. Middleware, die noch vor der Middleware, die die weitere Verarbeitung beendet, zur Pipeline hinzugefügt wird, verarbeitet Code noch nach den next.Invoke-Anweisungen weiter. Sehen Sie sich allerdings die folgende Warnung zum Versuch an, in eine Antwort zu schreiben, die bereits gesendet wurde.

Warnung

Rufen Sie next.Invoke nicht auf, nachdem die Antwort an den Client gesendet wurde. Änderungen an HttpResponse lösen nach dem Start der Antwort eine Ausnahme aus. Das Festlegen von Headern und einem Statuscode lösen beispielsweise eine Ausnahme aus. Wenn Sie nach dem Aufruf von next in den Antworttext schreiben, kann dies:

  • einen Protokollverstoß verursachen, Zum Beispiel, mehr als das angegebene Content-Length zu schreiben.
  • Fehler im Textformat auslösen, wenn Sie z.B. eine HTML-Fußzeile in eine CSS-Datei schreiben.

HasStarted ist ein nützlicher Hinweis, um anzuzeigen, ob Kopfzeilen gesendet wurden oder der Text geschrieben wurde.

Run-Delegaten erhalten keinen next-Parameter. Der erste Run-Delegat ist immer terminal und beendet die Pipeline. Run ist eine Konvention. Einige Middlewarekomponenten machen möglicherweise Run[Middleware]-Methoden verfügbar, die am Ende einer Pipeline ausgeführt werden:

app.Run(async context =>
{
    await context.Response.WriteAsync("Hello from 2nd delegate.");
});

Im vorherigen Beispiel schreibt der Run-Delegat "Hello from 2nd delegate." zur Antwort und beendet dann die Pipeline. Wenn ein anderer Use- oder Run-Delegat nach dem Run-Delegaten hinzugefügt wird, wird dieser nicht aufgerufen.

Verzweigen der Middleware-Pipeline

Map Erweiterungen werden als Konvention für die Verzweigung der Pipeline verwendet. Map brancht die Anforderungspipeline auf Grundlage von Übereinstimmungen des angegebenen Anforderungspfads. Wenn der Anfragepfad mit dem angegebenen Pfad beginnt, wird der Zweig ausgeführt.

public class Startup
{
    private static void HandleMapTest1(IApplicationBuilder app)
    {
        app.Run(async context =>
        {
            await context.Response.WriteAsync("Map Test 1");
        });
    }

    private static void HandleMapTest2(IApplicationBuilder app)
    {
        app.Run(async context =>
        {
            await context.Response.WriteAsync("Map Test 2");
        });
    }

    public void Configure(IApplicationBuilder app)
    {
        app.Map("/map1", HandleMapTest1);

        app.Map("/map2", HandleMapTest2);

        app.Run(async context =>
        {
            await context.Response.WriteAsync("Hello from non-Map delegate.");
        });
    }
}

In der folgenden Tabelle sind die Anfragen und Antworten von http://localhost:1234 mit dem vorherigen Code aufgelistet.

Anforderung Antwort
localhost:1234 Hello from non-Map delegate.
localhost:1234/map1 Kartentest 1
localhost:1234/map2 Kartentest 2
localhost:1234/map3 Hello from non-Map delegate.

Bei Verwendung von Map werden die übereinstimmenden Pfadsegmente aus HttpRequest.Path entfernt und für jede Anforderung an HttpRequest.PathBase angehängt.

Map unterstützt z. B. Verschachtelungen:

app.Map("/level1", level1App => {
    level1App.Map("/level2a", level2AApp => {
        // "/level1/level2a" processing
    });
    level1App.Map("/level2b", level2BApp => {
        // "/level1/level2b" processing
    });
});

Map kann auch mehrere Segmente auf einmal abgleichen:

public class Startup
{
    private static void HandleMultiSeg(IApplicationBuilder app)
    {
        app.Run(async context =>
        {
            await context.Response.WriteAsync("Map multiple segments.");
        });
    }

    public void Configure(IApplicationBuilder app)
    {
        app.Map("/map1/seg1", HandleMultiSeg);

        app.Run(async context =>
        {
            await context.Response.WriteAsync("Hello from non-Map delegate.");
        });
    }
}

MapWhen verzweigt die Anforderungspipeline basierend auf dem Ergebnis des angegebenen Prädikats. Sie können jedes Prädikat des Typs Func<HttpContext, bool> verwenden, um Anforderungen einem neuen Zweig der Pipeline zuzuordnen. Im folgenden Beispiel erkennt ein Prädikat das Vorhandensein einer Abfragezeichenfolgenvariable branch:

public class Startup
{
    private static void HandleBranch(IApplicationBuilder app)
    {
        app.Run(async context =>
        {
            var branchVer = context.Request.Query["branch"];
            await context.Response.WriteAsync($"Branch used = {branchVer}");
        });
    }

    public void Configure(IApplicationBuilder app)
    {
        app.MapWhen(context => context.Request.Query.ContainsKey("branch"),
                               HandleBranch);

        app.Run(async context =>
        {
            await context.Response.WriteAsync("Hello from non-Map delegate.");
        });
    }
}

In der folgenden Tabelle sind die Anfragen und Antworten von http://localhost:1234 mit dem vorherigen Code aufgelistet.

Anforderung Antwort
localhost:1234 Hello from non-Map delegate.
localhost:1234/?branch=main Branch used = main

UseWhen verzweigt auch die Anforderungspipeline basierend auf dem Ergebnis des angegebenen Prädikats. Anders als bei MapWhen wird dieser Branch wieder mit der Hauptpipeline verbunden, wenn er nicht kurzgeschlossen wird oder eine Terminalmiddleware enthält:

public class Startup
{
    private void HandleBranchAndRejoin(IApplicationBuilder app, ILogger<Startup> logger)
    {
        app.Use(async (context, next) =>
        {
            var branchVer = context.Request.Query["branch"];
            logger.LogInformation("Branch used = {branchVer}", branchVer.ToString());

            // Do work that doesn't write to the Response.
            await next();
            // Do other work that doesn't write to the Response.
        });
    }

    public void Configure(IApplicationBuilder app, ILogger<Startup> logger)
    {
        app.UseWhen(context => context.Request.Query.ContainsKey("branch"),
                               appBuilder => HandleBranchAndRejoin(appBuilder, logger));

        app.Run(async context =>
        {
            await context.Response.WriteAsync("Hello from main pipeline.");
        });
    }
}

Im vorherigen Beispiel wird die Antwort „Hello from main pipeline“ für alle Anforderungen ausgegeben. Wenn die Anforderung eine Abfragezeichenfolgevariable branch enthält, wird ihr Wert protokolliert, bevor die Hauptpipeline wiederhergestellt wird.

Middleware automatisch hinzugefügt von WebApplication

WebApplicationFügt die folgende Middleware automatisch in ASP.NET Core Apps hinzu, je nach bestimmten Bedingungen:

  • UseDeveloperExceptionPage wird zuerst hinzugefügt, wenn HostingEnvironment gleich "Development" ist.
  • UseRouting wird zweitens hinzugefügt, wenn der Benutzercode UseRouting noch nicht aufgerufen hat, und wenn Endpunkte konfiguriert sind, z. B. app.MapGet.
  • UseEndpoints wird am Ende der Middleware-Pipeline hinzugefügt, wenn mindestens ein Endpunkt konfiguriert ist.
  • UseAuthentication wird unmittelbar nach UseRouting hinzugefügt, wenn der Benutzercode UseAuthentication noch nicht aufgerufen hat und wenn IAuthenticationSchemeProvider im Dienstanbieter erkannt werden kann. IAuthenticationSchemeProvider wird standardmäßig hinzugefügt, wenn AddAuthentication verwendet wird, und Dienste werden mit IServiceProviderIsService erkannt.
  • UseAuthorization wird als Nächstes hinzugefügt, wenn der Benutzercode UseAuthorization noch nicht aufgerufen hat und wenn IAuthorizationHandlerProvider im Dienstanbieter erkannt werden kann. IAuthorizationHandlerProvider wird standardmäßig hinzugefügt, wenn AddAuthorization verwendet wird, und Dienste werden mit IServiceProviderIsService erkannt.
  • Benutzerkonfigurierte Middleware und Endpunkte werden zwischen UseRouting und UseEndpoints hinzugefügt.

Der folgende Code entspricht effektiv dem, was die automatische Middleware, die zur App hinzugefügt wird, erzeugt.

if (isDevelopment)
{
    app.UseDeveloperExceptionPage();
}

app.UseRouting();

if (isAuthenticationConfigured)
{
    app.UseAuthentication();
}

if (isAuthorizationConfigured)
{
    app.UseAuthorization();
}

// user middleware/endpoints
app.CustomMiddleware(...);
app.MapGet("/", () => "hello world");
// end user middleware/endpoints

app.UseEndpoints(e => {});

In einigen Fällen eignet sich die standardmäßige Middleware-Konfiguration nicht für die App und muss geändert werden. Beispielsweise sollte UseCors vor UseAuthentication und UseAuthorization aufgerufen werden. Die App muss UseAuthentication und UseAuthorization aufrufen, wenn UseCors aufgerufen wird:

app.UseCors();
app.UseAuthentication();
app.UseAuthorization();

Wenn Middleware ausgeführt werden muss, bevor der Routenabgleich erfolgt, muss UseRouting aufgerufen werden, und die Middleware muss vor dem Aufruf von UseRouting platziert werden. UseEndpoints ist in diesem Fall nicht erforderlich, da es wie zuvor beschrieben automatisch hinzugefügt wird:

app.Use((context, next) =>
{
    return next(context);
});

app.UseRouting();

// other middleware and endpoints

Beim Hinzufügen einer Terminal-Middleware:

  • Die Middleware muss nach UseEndpoints hinzugefügt werden.
  • Die App muss UseRouting und UseEndpoints aufrufen, damit die Terminal-Middleware an der richtigen Position platziert werden kann.
app.UseRouting();

app.MapGet("/", () => "hello world");

app.UseEndpoints(e => {});

app.Run(context =>
{
    context.Response.StatusCode = 404;
    return Task.CompletedTask;
});

Terminal-Middleware ist Middleware, die ausgeführt wird, wenn kein Endpunkt die Anforderung verarbeitet.

WebApplication fügt automatisch die folgende Middleware in ASP.NET Core Apps hinzu, je nach bestimmten Bedingungen:

  • UseDeveloperExceptionPage wird zuerst hinzugefügt, wenn das HostingEnvironment"Development" ist.

  • UseRouting wird als zweites hinzugefügt, wenn der Benutzercode nicht bereits UseRouting aufgerufen hat und Endpunkte konfiguriert sind, z. B. app.MapGet.

  • UseEndpoints wird am Ende der Middleware-Pipeline hinzugefügt, wenn Endpunkte konfiguriert sind.

  • UseAuthentication wird unmittelbar nach UseRouting hinzugefügt, wenn der Benutzercode nicht bereits UseAuthentication aufgerufen hat und wenn IAuthenticationSchemeProvider im Dienstanbieter erkannt werden kann. IAuthenticationSchemeProvider wird standardmäßig hinzugefügt, wenn Sie AddAuthentication verwenden, und Dienste werden mithilfe von IServiceProviderIsService erkannt.

  • UseAuthorization wird als Nächstes hinzugefügt, wenn der Benutzercode noch nicht UseAuthorization aufgerufen hat und wenn IAuthorizationHandlerProvider im Dienstanbieter erkannt werden kann. IAuthorizationHandlerProvider wird standardmäßig hinzugefügt, wenn Sie AddAuthorization verwenden, und Dienste werden mithilfe von IServiceProviderIsService ermittelt.

  • Benutzerkonfigurierte Middleware und Endpunkte werden zwischen UseRouting und UseEndpoints hinzugefügt.

Der folgende Code entspricht effektiv dem, was die automatische Middleware, die zur App hinzugefügt wird, erzeugt.

if (isDevelopment)
{
    app.UseDeveloperExceptionPage();
}

app.UseRouting();

if (isAuthenticationConfigured)
{
    app.UseAuthentication();
}

if (isAuthorizationConfigured)
{
    app.UseAuthorization();
}

// User middleware/endpoints
app.CustomMiddleware(...);
app.MapGet("/", () => "hello world");
// End user middleware/endpoints

app.UseEndpoints(e => {});

In einigen Fällen eignet sich die standardmäßige Middleware-Konfiguration nicht für die App und muss geändert werden. Beispielsweise sollte UseCors vor UseAuthentication und UseAuthorization aufgerufen werden. Die App muss UseAuthentication und UseAuthorization aufrufen, wenn UseCors aufgerufen wird:

app.UseCors();
app.UseAuthentication();
app.UseAuthorization();

Wenn Middleware ausgeführt werden muss, bevor der Routenabgleich erfolgt, muss UseRouting aufgerufen werden, und die Middleware muss vor dem Aufruf von UseRouting platziert werden. UseEndpoints ist in diesem Fall nicht erforderlich, da sie wie zuvor beschrieben automatisch hinzugefügt wird:

app.Use((context, next) =>
{
    return next(context);
});

app.UseRouting();

// Other middleware and endpoints

Beim Hinzufügen einer Terminal-Middleware:

  • Die Middleware muss nach UseEndpoints hinzugefügt werden.

  • Die App muss UseRouting und UseEndpoints aufrufen, damit die Terminal-Middleware an der richtigen Stelle platziert werden kann.

app.UseRouting();

app.MapGet("/", () => "hello world");

app.UseEndpoints(e => {});

app.Run(context =>
{
    context.Response.StatusCode = 404;
    return Task.CompletedTask;
});

Terminal-Middleware ist Middleware, die ausgeführt wird, wenn kein Endpunkt die Anforderung verarbeitet.

Informationen zu Antiforgery-Middleware in minimalen APIs finden Sie unter Prevent Cross-Site Request Forgery (XSRF/CSRF)-Angriffe in ASP.NET Core.

Reihenfolge der Middleware

Die Reihenfolge, in der Middleware in der App-Datei Program angezeigt wird, definiert die Reihenfolge, in der Middleware für eine Anforderung mit der umgekehrten Reihenfolge für die Antwort aufgerufen wird.

Sie haben die volle Kontrolle über die Reihenfolge der Middleware und die Möglichkeit, benutzerdefinierte Middleware für Anforderungsverarbeitungsszenarien hinzuzufügen, und beachten Sie, dass die Reihenfolge der Middleware für Sicherheit, Leistung und Funktionalität von entscheidender Bedeutung sein kann.

Die folgenden Beispiele veranschaulichen die Middlewarereihenfolge für allgemeine App-Szenarien. Jede Middleware-Erweiterungsmethode wird im WebApplicationBuilder-Namespace über Microsoft.AspNetCore.Builder verfügbar gemacht.

  1. Ausnahme- und Fehlerbehandlung
    • Wenn die App in der Development Umgebung ausgeführt wird:
    • Wenn die App in der Production Umgebung ausgeführt wird:
      • Ausnahmehandler Middleware (UseExceptionHandler) fängt Ausnahmen ab, die in den folgenden Middlewares ausgelöst werden.
      • HTTP Strict Transport Security (HSTS)-Protokoll-Middleware (UseHsts) fügt den Strict-Transport-Security Header hinzu.
  2. HTTPS-Umleitungs-Middleware (UseHttpsRedirection) leitet HTTP-Anforderungen an HTTPS um.
  3. Middleware für statische Dateien (falls erforderlich, UseStaticFiles) gibt statische Dateien zurück und beendet die weitere Anforderungsverarbeitung vorzeitig.
  4. Cookie Policy Middleware (UseCookiePolicy) entspricht der App der EU-Datenschutz-Grundverordnung (DSGVO).
  5. Routing-Middleware (UseRouting) zum Weiterleiten von Anfragen.
  6. Authentifizierungs-Middleware (UseAuthentication) versucht, den Benutzer zu authentifizieren, bevor er Zugriff auf sichere Ressourcen hat.
  7. Autorisierungs-Middleware (UseAuthorization) autorisiert einen Benutzer für den Zugriff auf sichere Ressourcen.
  8. Das Antiforgery-Middleware (UseAntiforgery) fügt der Pipeline UseAntiforgery Antiforgery-Middleware hinzu und muss hinter den Aufrufen von UseAuthentication und UseAuthorization platziert werden.
  9. Die Session-Middleware (Razor nur Pages und MVC, UseSession) richtet den Sitzungszustand ein und verwaltet ihn. Wenn die App den Sitzungsstatus verwendet, rufen Sie die Sitzungs-Middleware nach der cookie-Policy-Middleware und vor der Middleware für Razor Pages/MVC auf.
  10. Middleware für Endpunktrouting
  • MapRazorComponents zum Hinzufügen von Razor Komponentenendpunkten zur Anforderungspipeline.
  • MapRazorPages zum Hinzufügen von Razor Pages-Endpoints zur Anforderungspipeline.
  • MapControllerRoute zum Hinzufügen von Controllerendpunkten zur Anforderungspipeline.

Typische Blazor Web App Middleware-Pipeline:

app.UseWebAssemblyDebugging(); // Development environment with client-side rendering
app.UseMigrationsEndPoint(); // Development environment with ASP.NET Core Identity

app.UseExceptionHandler("/Error", createScopeForErrors: true); // Non-Development environment
app.UseHsts(); // Non-Development environment with HTTPS protocol

app.UseStatusCodePagesWithReExecute("/not-found", createScopeForStatusCodePages: true);

app.UseHttpsRedirection(); // With HTTPS protocol

app.UseAntiforgery();

app.MapStaticAssets();

app.MapRazorComponents<App>(); // With additional extension methods for render modes

app.MapAdditionalIdentityEndpoints(); // With ASP.NET Core Identity

app.Run();

Typische Razor Pages/MVC Middleware-Pipeline:

app.UseMigrationsEndPoint(); // Development environment with ASP.NET Core Identity

app.UseExceptionHandler("/Error"); // Non-Development environment
app.UseHsts(); // Non-Development environment with HTTPS protocol

app.UseHttpsRedirection(); // With HTTPS protocol

// app.UseCookiePolicy();
app.UseRouting(); // If not called, runs at the beginning of the pipeline by default
// app.UseRateLimiter();
// app.UseRequestLocalization();
// app.UseCors();

// app.UseAuthentication(); // Called internally for ASP.NET Core Identity
app.UseAuthorization();
// app.UseSession();
// app.UseResponseCompression();
// app.UseResponseCaching();

app.MapStaticAssets();

app.MapControllerRoute(...); // For MVC controllers

app.MapRazorPages(); // For Razor Pages pages

app.MapControllers(); // With authentication in a Razor Pages app

app.Run();

Im vorhergehenden Code:

In einigen Szenarien weist die Middleware eine andere Reihenfolge auf. Beispielsweise hängt die Zwischenspeicherung und Komprimierungsordnung von der Spezifikation der App ab. In der folgenden Reihenfolge kann die CPU-Auslastung reduziert werden, indem die komprimierte Antwort zwischengespeichert wird. Die App kann jedoch mehrere Darstellungen einer Ressource mit unterschiedlichen Komprimierungsalgorithmen wie Gzip oder Brotli zwischenspeichern:

app.UseResponseCaching();
app.UseResponseCompression();

Statische Ressourcen werden in der Regel frühzeitig in der Pipeline bereitgestellt, sodass die App die Verarbeitung von Anfragen abkürzen kann, um die Leistung zu verbessern.

Die Authentifizierung schließt nicht authentifizierte Anforderungen nicht kurz. Obwohl Authentifizierungsmiddleware Anfragen authentifiziert, erfolgt die Autorisierung erst, nachdem das Framework eine Razor-Komponente in einer Blazor Web App-App, eine Seite in einer Razor-App oder einen Controller und eine Aktion in einer MVC-App ausgewählt hat.

In der folgenden Abbildung wird die gesamte Anforderungsverarbeitungspipeline für MVC- und Razor Pages-Apps in ASP.NET Core dargestellt. Es ist zu sehen, wie vorhandene Middleware in einer typischen App sortiert ist und an welcher Stelle benutzerdefinierte Middleware hinzugefügt wird. Sie haben vollständige Kontrolle darüber, wie vorhandene Middleware-Komponenten neu angeordnet oder neue benutzerdefinierte Middleware-Komponenten nach Bedarf eingefügt werden.

ASP.NET Core-Middleware-Pipeline

Der Endpunktmiddleware in der vorangehenden Abbildung führt die Filterpipeline für den entsprechenden App-Typ aus (MVC oder Razor Pages).

Das vorangehende Diagramm zeigt, dass die Routing-Middleware auf statische Dateien folgt. In dieser Reihenfolge funktionieren die Projektvorlagen, indem app.UseRouting explizit aufgerufen wird. Wenn Sie nicht app.UseRouting aufrufen, wird die Routing-Middleware standardmäßig am Anfang der Pipeline ausgeführt. Weitere Informationen finden Sie unter Routing.

Die Reihenfolge, in der Sie Middleware-Komponenten in der Program.cs Datei hinzufügen, definiert die Reihenfolge, in der die Middleware-Komponenten für Anforderungen und die umgekehrte Reihenfolge für die Antwort aufgerufen werden. Die Reihenfolge ist in Bezug auf Sicherheit, Leistung und Funktionalität entscheidend.

Der folgende hervorgehobene Code in Program.cs fügt sicherheitsbezogene Middlewarekomponenten in der typischen empfohlenen Reihenfolge hinzu:

using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using WebMiddleware.Data;

var builder = WebApplication.CreateBuilder(args);

var connectionString = builder.Configuration.GetConnectionString("DefaultConnection")
    ?? throw new InvalidOperationException("Connection string 'DefaultConnection' not found.");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlServer(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();

builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
    .AddEntityFrameworkStores<ApplicationDbContext>();
builder.Services.AddRazorPages();
builder.Services.AddControllersWithViews();

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.UseMigrationsEndPoint();
}
else
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();
// app.UseCookiePolicy();

app.UseRouting();
// app.UseRateLimiter();
// app.UseRequestLocalization();
// app.UseCors();

app.UseAuthentication();
app.UseAuthorization();
// app.UseSession();
// app.UseResponseCompression();
// app.UseResponseCaching();

app.MapRazorPages();
app.MapDefaultControllerRoute();

app.Run();

Im vorhergehenden Code:

  • Auskommentierte Middleware wird nicht hinzugefügt, wenn Sie eine neue Web-App mit einzelnen Benutzerkonten erstellen.
  • Die exakte Reihenfolge ist nicht für jede Middleware vorgeschrieben (dies ist jedoch meistens der Fall). Beispiel:
    • UseCors, UseAuthentication und UseAuthorization müssen in der angezeigten Reihenfolge stehen.
    • UseCors muss derzeit vor UseResponseCaching stehen. Diese Anforderung wird im GitHub-Issue zu dotnet/aspnetcore, Nr. 23218, erläutert.
    • UseRequestLocalization muss vor jeder Middleware stehen, die u. U. die Anforderungskultur überprüft (z. B. app.UseStaticFiles()).
    • UseRateLimiter muss nach UseRouting aufgerufen werden, wenn endpunktspezifische APIs für die Ratenbegrenzung verwendet werden. Wenn zum Beispiel das Attribut [EnableRateLimiting] verwendet wird, muss UseRateLimiter nach UseRouting aufgerufen werden. Wenn nur globale Begrenzungen aufgerufen werden, UseRateLimiter kann vor UseRouting aufgerufen werden.

In einigen Szenarien weist die Middleware eine andere Reihenfolge auf. Die Reihenfolge für Zwischenspeicherung und Komprimierung beispielsweise ist szenariospezifisch, und es gibt verschiedene gültige Reihenfolgen. Beispiel:

app.UseResponseCaching();
app.UseResponseCompression();

Mit dem vorherigen Code können Sie die CPU-Auslastung reduzieren, indem Sie die komprimierte Antwort zwischenspeichern, aber Sie können mehrere Darstellungen einer Ressource mithilfe verschiedener Komprimierungsalgorithmen wie Gzip oder Brotli zwischenspeichern.

Die folgende Reihenfolge kombiniert statische Dateien, um eine Zwischenspeicherung komprimierter statischer Dateien zuzulassen:

app.UseResponseCaching();
app.UseResponseCompression();
app.UseStaticFiles();

Der folgende Program.cs-Code fügt Middlewarekomponenten für allgemeine App-Szenarios hinzu:

  1. Ausnahme- und Fehlerbehandlung
    • Wenn die App in der Development Umgebung ausgeführt wird:
    • Wenn die App in der Production Umgebung ausgeführt wird:
      • Ausnahmehandler Middleware (UseExceptionHandler) fängt Ausnahmen ab, die in den folgenden Middlewares ausgelöst werden.
      • HTTP Strict Transport Security (HSTS)-Protokoll-Middleware (UseHsts) fügt den Strict-Transport-Security Header hinzu.
  2. HTTPS-Umleitungs-Middleware (UseHttpsRedirection) leitet HTTP-Anforderungen an HTTPS um.
  3. Die Middleware für statische Dateien (UseStaticFiles) gibt statische Dateien zurück und beendet die weitere Anforderungsverarbeitung vorzeitig.
  4. Cookie Richtlinien-Middleware (UseCookiePolicy) bringt die App mit den Vorgaben der EU-Datenschutz-Grundverordnung (DSGVO) in Einklang.
  5. Routing-Middleware (UseRouting) zum Weiterleiten von Anfragen.
  6. Authentifizierungs-Middleware (UseAuthentication) versucht, den Benutzer zu authentifizieren, bevor er Zugriff auf sichere Ressourcen hat.
  7. Autorisierungs-Middleware (UseAuthorization) autorisiert einen Benutzer für den Zugriff auf sichere Ressourcen.
  8. Die Sessionmiddleware (UseSession) initialisiert und verwaltet den Sitzungszustand. Wenn die App den Sitzungsstatus verwendet, rufen Sie die Middleware für Sitzungen nach der Middleware für cookie-Richtlinien und vor der MVC-Middleware auf.
  9. Endpunktroutingmiddleware (UseEndpoints mit MapRazorPages) zum Hinzufügen von Razor Pages-Endpunkten zur Anforderungspipeline.
if (env.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
    app.UseDatabaseErrorPage();
}
else
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseSession();
app.MapRazorPages();

Im vorhergehenden Beispielcode wird jede Middleware-Erweiterungsmethode in WebApplicationBuilder über den Microsoft.AspNetCore.Builder-Namespace verfügbar gemacht.

UseExceptionHandler ist die erste Middlewarekomponente, die der Pipeline hinzugefügt wird. Daher fängt die Middleware des Ausnahmehandlers alle Ausnahmen ab, die in späteren Aufrufen auftreten.

Die Middleware für statische Dateien wird am Anfang der Pipeline aufgerufen, damit sie Anforderungen und Kurzschlüsse verarbeiten kann, ohne dass die verbleibenden Komponenten durchlaufen werden müssen. Die statische Datei-Middleware stellt keine Autorisierungsprüfungen bereit. Alle Dateien, die von statischer Datei-Middleware bereitgestellt werden, einschließlich der Dateien unter wwwroot, sind öffentlich verfügbar. Im Artikel zu statischen Dateien in ASP.NET Core erfahren Sie, wie Sie statische Dateien schützen können.

Wenn die Anforderung nicht von der statischen Datei-Middleware behandelt wird, wird sie an die Authentifizierungs-Middleware (UseAuthentication) übergeben, die Authentifizierung durchführt. Die Authentifizierung schließt nicht authentifizierte Anforderungen nicht kurz. Obwohl die Authentifizierungs-Middleware Anforderungen authentifiziert, tritt autorisierung (und Ablehnung) erst auf, nachdem MVC einen bestimmten Page- oder MVC-Controller und eine bestimmte Razor Aktion ausgewählt hat.

Im folgenden Beispiel wird eine Middleware-Reihenfolge veranschaulicht, in der Anforderungen für statische Dateien von statischer Datei-Middleware vor der Middleware für die Reaktionskomprimierung behandelt werden. Die statischen Dateien werden in dieser Reihenfolge der Middleware nicht komprimiert. Die Razor Pages-Antworten können komprimiert werden.

// Static files aren't compressed by static file middleware.
app.UseStaticFiles();

app.UseRouting();

app.UseResponseCompression();

app.MapRazorPages();

In der folgenden Abbildung wird die gesamte Anforderungsverarbeitungspipeline für MVC- und Razor Pages-Apps in ASP.NET Core dargestellt. Es ist zu sehen, wie vorhandene Middleware in einer typischen App sortiert ist und an welcher Stelle benutzerdefinierte Middleware hinzugefügt wird. Sie haben vollständige Kontrolle darüber, wie vorhandene Middleware-Komponenten neu angeordnet oder neue benutzerdefinierte Middleware-Komponenten nach Bedarf eingefügt werden.

ASP.NET Core-Middleware-Pipeline

Der Endpunktmiddleware in der vorangehenden Abbildung führt die Filterpipeline für den entsprechenden App-Typ aus (MVC oder Razor Pages).

Das vorstehende Diagramm zeigt, dass die Routing-Middleware auf statische Dateien folgt. In dieser Reihenfolge funktionieren die Projektvorlagen durch explizite Aufrufe von app.UseRouting. Wenn Sie nicht app.UseRouting aufrufen, wird die Routing-Middleware standardmäßig am Anfang der Pipeline ausgeführt. Weitere Informationen finden Sie unter Routing.

Die Reihenfolge, in der Sie Middleware-Komponenten in der Program.cs Datei hinzufügen, definiert die Reihenfolge, in der die Middleware-Komponenten für Anforderungen und die umgekehrte Reihenfolge für die Antwort aufgerufen werden. Die Reihenfolge ist in Bezug auf Sicherheit, Leistung und Funktionalität entscheidend.

Der folgende hervorgehobene Code in Program.cs fügt sicherheitsbezogene Middlewarekomponenten in der typischen empfohlenen Reihenfolge hinzu:

using IndividualAccountsExample.Data;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlServer(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();

builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
    .AddEntityFrameworkStores<ApplicationDbContext>();
builder.Services.AddRazorPages();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseMigrationsEndPoint();
}
else
{
    app.UseExceptionHandler("/Error");
    // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();
// app.UseCookiePolicy();

app.UseRouting();
// app.UseRequestLocalization();
// app.UseCors();

app.UseAuthentication();
app.UseAuthorization();
// app.UseSession();
// app.UseResponseCompression();
// app.UseResponseCaching();

app.MapRazorPages();
app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");

app.Run();

Im vorhergehenden Code:

  • Auskommentierte Middleware wird nicht hinzugefügt, wenn Sie eine neue Web-App mit einzelnen Benutzerkonten erstellen.
  • Die exakte Reihenfolge ist nicht für jede Middleware vorgeschrieben (dies ist jedoch meistens der Fall). Beispiel:
    • UseCors, UseAuthentication und UseAuthorization müssen in der angezeigten Reihenfolge stehen.
    • UseCors muss derzeit vor UseResponseCaching stehen. Diese Anforderung wird im GitHub-Issue zu dotnet/aspnetcore, Nr. 23218, erläutert.
    • UseRequestLocalization muss vor jeder Middleware stehen, die u. U. die Anforderungskultur überprüft (z. B. app.UseMvcWithDefaultRoute()).

In einigen Szenarien weist die Middleware eine andere Reihenfolge auf. Die Reihenfolge für Zwischenspeicherung und Komprimierung beispielsweise ist szenariospezifisch, und es gibt verschiedene gültige Reihenfolgen. Beispiel:

app.UseResponseCaching();
app.UseResponseCompression();

Mit dem vorherigen Code können Sie die CPU-Auslastung reduzieren, indem Sie die komprimierte Antwort zwischenspeichern, aber Sie können mehrere Darstellungen einer Ressource mithilfe verschiedener Komprimierungsalgorithmen wie Gzip oder Brotli zwischenspeichern.

Die folgende Reihenfolge kombiniert statische Dateien, um eine Zwischenspeicherung komprimierter statischer Dateien zuzulassen:

app.UseResponseCaching();
app.UseResponseCompression();
app.UseStaticFiles();

Der folgende Program.cs-Code fügt Middlewarekomponenten für allgemeine App-Szenarios hinzu:

  1. Ausnahme- und Fehlerbehandlung
    • Wenn die App in der Development Umgebung ausgeführt wird:
    • Wenn die App in der Production Umgebung ausgeführt wird:
      • Ausnahmehandler Middleware (UseExceptionHandler) fängt Ausnahmen ab, die in den folgenden Middlewares ausgelöst werden.
      • HTTP Strict Transport Security (HSTS)-Protokoll-Middleware (UseHsts) fügt den Strict-Transport-Security Header hinzu.
  2. HTTPS-Umleitungs-Middleware (UseHttpsRedirection) leitet HTTP-Anforderungen an HTTPS um.
  3. Die Middleware für statische Dateien (UseStaticFiles) gibt statische Dateien zurück und beendet die weitere Anforderungsverarbeitung vorzeitig.
  4. Cookie Richtlinien-Middleware (UseCookiePolicy) bringt die App mit den Vorgaben der EU-Datenschutz-Grundverordnung (DSGVO) in Einklang.
  5. Routing-Middleware (UseRouting) zum Weiterleiten von Anfragen.
  6. Authentifizierungs-Middleware (UseAuthentication) versucht, den Benutzer zu authentifizieren, bevor er Zugriff auf sichere Ressourcen hat.
  7. Autorisierungs-Middleware (UseAuthorization) autorisiert einen Benutzer für den Zugriff auf sichere Ressourcen.
  8. Die Sessionmiddleware (UseSession) initialisiert und verwaltet den Sitzungszustand. Wenn die App den Sitzungsstatus verwendet, rufen Sie die Middleware für Sitzungen nach der Middleware für cookie-Richtlinien und vor der MVC-Middleware auf.
  9. Endpunktroutingmiddleware (UseEndpoints mit MapRazorPages) zum Hinzufügen von Razor Pages-Endpunkten zur Anforderungspipeline.
if (env.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
    app.UseDatabaseErrorPage();
}
else
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseSession();
app.MapRazorPages();

Im vorhergehenden Beispielcode wird jede Middleware-Erweiterungsmethode in WebApplicationBuilder über den Microsoft.AspNetCore.Builder-Namespace verfügbar gemacht.

UseExceptionHandler ist die erste Middlewarekomponente, die der Pipeline hinzugefügt wird. Daher fängt die Middleware des Ausnahmehandlers alle Ausnahmen ab, die in späteren Aufrufen auftreten.

Die Middleware für statische Dateien wird am Anfang der Pipeline aufgerufen, damit sie Anforderungen und Kurzschlüsse verarbeiten kann, ohne dass die verbleibenden Komponenten durchlaufen werden müssen. Die statische Datei-Middleware stellt keine Autorisierungsprüfungen bereit. Alle Dateien, die von statischer Datei-Middleware bereitgestellt werden, einschließlich der Dateien unter wwwroot, sind öffentlich verfügbar. Im Artikel zu statischen Dateien in ASP.NET Core erfahren Sie, wie Sie statische Dateien schützen können.

Wenn die Anforderung nicht von der statischen Datei-Middleware behandelt wird, wird sie an die Authentifizierungs-Middleware (UseAuthentication) übergeben, die Authentifizierung durchführt. Die Authentifizierung schließt nicht authentifizierte Anforderungen nicht kurz. Obwohl die Authentifizierungs-Middleware Anforderungen authentifiziert, tritt autorisierung (und Ablehnung) erst auf, nachdem MVC einen bestimmten Page- oder MVC-Controller und eine bestimmte Razor Aktion ausgewählt hat.

Im folgenden Beispiel wird eine Middleware-Reihenfolge veranschaulicht, in der Anforderungen für statische Dateien von statischer Datei-Middleware vor der Middleware für die Reaktionskomprimierung behandelt werden. Die statischen Dateien werden in dieser Reihenfolge der Middleware nicht komprimiert. Die Razor Pages-Antworten können komprimiert werden.

// Static files aren't compressed by static file middleware.
app.UseStaticFiles();

app.UseRouting();

app.UseResponseCompression();

app.MapRazorPages();

In der folgenden Abbildung wird die gesamte Anforderungsverarbeitungspipeline für MVC- und Razor Pages-Apps in ASP.NET Core dargestellt. Es ist zu sehen, wie vorhandene Middleware in einer typischen App sortiert ist und an welcher Stelle benutzerdefinierte Middleware hinzugefügt wird. Sie haben vollständige Kontrolle darüber, wie vorhandene Middleware-Komponenten neu angeordnet oder neue benutzerdefinierte Middleware-Komponenten nach Bedarf eingefügt werden.

ASP.NET Core-Middleware-Pipeline

Der Endpunktmiddleware in der vorangehenden Abbildung führt die Filterpipeline für den entsprechenden App-Typ aus (MVC oder Razor Pages).

Die Reihenfolge, in der Middlewarekomponenten in der Startup.Configure-Methode hinzugefügt werden, legt die Reihenfolge fest, in der die Middlewarekomponenten bei Anforderungen aufgerufen werden. Bei Antworten gilt die umgekehrte Reihenfolge. Die Reihenfolge ist in Bezug auf Sicherheit, Leistung und Funktionalität entscheidend.

Die folgende Startup.Configure-Methode fügt sicherheitsbezogene Middlewarekomponenten in der typischen empfohlenen Reihenfolge hinzu:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
        app.UseDatabaseErrorPage();
    }
    else
    {
        app.UseExceptionHandler("/Error");
        app.UseHsts();
    }

    app.UseHttpsRedirection();
    app.UseStaticFiles();
    // app.UseCookiePolicy();

    app.UseRouting();
    // app.UseRequestLocalization();
    // app.UseCors();

    app.UseAuthentication();
    app.UseAuthorization();
    // app.UseSession();
    // app.UseResponseCompression();
    // app.UseResponseCaching();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapRazorPages();
        endpoints.MapControllerRoute(
            name: "default",
            pattern: "{controller=Home}/{action=Index}/{id?}");
    });
}

Im vorhergehenden Code:

  • Auskommentierte Middleware wird nicht hinzugefügt, wenn Sie eine neue Web-App mit einzelnen Benutzerkonten erstellen.
  • Die exakte Reihenfolge ist nicht für jede Middleware vorgeschrieben (dies ist jedoch meistens der Fall). Beispiel:
    • UseCors, UseAuthentication und UseAuthorization müssen in der angezeigten Reihenfolge stehen.
    • UseCors muss derzeit vor UseResponseCaching stehen, aufgrund dieses Bugs.
    • UseRequestLocalization muss vor jeder Middleware stehen, die u. U. die Anforderungskultur überprüft (z. B. app.UseMvcWithDefaultRoute()).

In einigen Szenarien weist die Middleware eine andere Reihenfolge auf. Die Reihenfolge für Zwischenspeicherung und Komprimierung beispielsweise ist szenariospezifisch, und es gibt verschiedene gültige Reihenfolgen. Beispiel:

app.UseResponseCaching();
app.UseResponseCompression();

Mit dem obigen Code könnte durch Zwischenspeichern der komprimierten Antwort die CPU-Auslastung verringert werden, dies könnte aber dazu führen, dass mehrere Darstellungen einer Ressource mithilfe verschiedener Komprimierungsalgorithmen wie z. B. Gzip oder Brotli zwischengespeichert werden.

Die folgende Reihenfolge kombiniert statische Dateien, um eine Zwischenspeicherung komprimierter statischer Dateien zuzulassen:

app.UseResponseCaching();
app.UseResponseCompression();
app.UseStaticFiles();

Die folgenden Startup.Configure-Methode fügt Middlewarekomponenten für allgemeine App-Szenarien hinzu:

  1. Ausnahme- und Fehlerbehandlung
    • Wenn die App in der Development Umgebung ausgeführt wird:
      • Die Middleware für Entwicklerausnahmeseiten (UseDeveloperExceptionPage) zeigt Laufzeitfehler der App an.
      • Die Middleware-Datenbankfehlerseite meldet Datenbanklaufzeitfehler.
    • Wenn die App in der Production Umgebung ausgeführt wird:
      • Ausnahmehandler Middleware (UseExceptionHandler) fängt Ausnahmen ab, die in den folgenden Middlewares ausgelöst werden.
      • HTTP Strict Transport Security (HSTS)-Protokoll-Middleware (UseHsts) fügt den Strict-Transport-Security Header hinzu.
  2. HTTPS-Umleitungs-Middleware (UseHttpsRedirection) leitet HTTP-Anforderungen an HTTPS um.
  3. Die Middleware für statische Dateien (UseStaticFiles) gibt statische Dateien zurück und beendet die weitere Anforderungsverarbeitung vorzeitig.
  4. Cookie Richtlinien-Middleware (UseCookiePolicy) bringt die App mit den Vorgaben der EU-Datenschutz-Grundverordnung (DSGVO) in Einklang.
  5. Routing-Middleware (UseRouting) zum Weiterleiten von Anfragen.
  6. Authentifizierungs-Middleware (UseAuthentication) versucht, den Benutzer zu authentifizieren, bevor er Zugriff auf sichere Ressourcen hat.
  7. Autorisierungs-Middleware (UseAuthorization) autorisiert einen Benutzer für den Zugriff auf sichere Ressourcen.
  8. Die Sessionmiddleware (UseSession) initialisiert und verwaltet den Sitzungszustand. Wenn die App den Sitzungsstatus verwendet, rufen Sie die Middleware für Sitzungen nach der Middleware für cookie-Richtlinien und vor der MVC-Middleware auf.
  9. Endpunktroutingmiddleware (UseEndpoints mit MapRazorPages) zum Hinzufügen von Razor Pages-Endpunkten zur Anforderungspipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
        app.UseDatabaseErrorPage();
    }
    else
    {
        app.UseExceptionHandler("/Error");
        app.UseHsts();
    }

    app.UseHttpsRedirection();
    app.UseStaticFiles();
    app.UseCookiePolicy();
    app.UseRouting();
    app.UseAuthentication();
    app.UseAuthorization();
    app.UseSession();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapRazorPages();
    });
}

Im vorhergehenden Beispielcode wird jede Middleware-Erweiterungsmethode in IApplicationBuilder über den Microsoft.AspNetCore.Builder-Namespace verfügbar gemacht.

UseExceptionHandler ist die erste Middlewarekomponente, die der Pipeline hinzugefügt wird. Daher fängt die Middleware des Ausnahmehandlers alle Ausnahmen ab, die in späteren Aufrufen auftreten.

Die Middleware für statische Dateien wird am Anfang der Pipeline aufgerufen, damit sie Anforderungen und Kurzschlüsse verarbeiten kann, ohne dass die verbleibenden Komponenten durchlaufen werden müssen. Die statische Datei-Middleware stellt keine Autorisierungsprüfungen bereit. Alle Dateien, die von statischer Datei-Middleware bereitgestellt werden, einschließlich der Dateien unter wwwroot, sind öffentlich verfügbar. Im Artikel zu statischen Dateien in ASP.NET Core erfahren Sie, wie Sie statische Dateien schützen können.

Wenn die Anforderung nicht von der statischen Datei-Middleware behandelt wird, wird sie an die Authentifizierungs-Middleware (UseAuthentication) übergeben, die Authentifizierung durchführt. Die Authentifizierung schließt nicht authentifizierte Anforderungen nicht kurz. Obwohl die Authentifizierungs-Middleware Anforderungen authentifiziert, tritt autorisierung (und Ablehnung) erst auf, nachdem MVC einen bestimmten Page- oder MVC-Controller und eine bestimmte Razor Aktion ausgewählt hat.

Im folgenden Beispiel wird eine Middleware-Reihenfolge veranschaulicht, in der Anforderungen für statische Dateien von statischer Datei-Middleware vor der Middleware für die Reaktionskomprimierung behandelt werden. Die statischen Dateien werden in dieser Reihenfolge der Middleware nicht komprimiert. Die Razor Pages-Antworten können komprimiert werden.

public void Configure(IApplicationBuilder app)
{
    // Static files aren't compressed by static file middleware.
    app.UseStaticFiles();

    app.UseRouting();

    app.UseResponseCompression();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapRazorPages();
    });
}

Bei Single-Page-Webanwendungen (Single-Page Applications, SPAs) ist die entsprechende SPA-Middleware UseSpaStaticFiles in der Regel der letzte Teil der Middlewarepipeline. Die SPA-Middleware steht an letzter Stelle.

  • Damit alle anderen Middleware zuerst auf übereinstimmende Anforderungen reagieren können.
  • Damit SPAs mit clientseitigem Routing für alle Routen ausgeführt werden können, die von der Server-App nicht erkannt werden.

Weitere Informationen zu SPAs finden Sie in den Anleitungen zu den React- und Angular-Projektvorlagen.

Weitere Informationen zu Single-Page-Anwendungen finden Sie in den Anleitungen zu den React- und Angular-Projektvorlagen.

UseCors und UseStaticFiles Reihenfolge

Weitere Informationen zum Bestellen von UseCors und UseStaticFiles finden Sie unter Aktivieren von Cross-Origin Requests (CORS) in ASP.NET Core.

Middleware für weitergeleitete Header: Auftrag

Führen Sie die Middleware für weitergeleitete Header vor anderer Middleware aus, um sicherzustellen, dass Middleware, die auf Informationen aus weitergeleiteten Headern angewiesen ist, die Headerwerte zur Verarbeitung nutzen kann. Wenn Sie wissen möchten, wie Middleware für Weitergeleitete Header nach Middleware für Diagnose und Fehlerbehebung ausgeführt wird, finden Sie weitere Informationen unter Middleware für weitergeleitete Header – Reihenfolge.

Integrierte Middleware

Die neueste Version von ASP.NET Core enthält die folgende Middleware. Die Spalte für den UI-Stapel zeigt den typischen UI-Stapel an, in dem die Middleware verwendet wird: [Alle, Blazor Web App (BWA), Razor Seiten und MVC (RP/MVC)]. Die Spalte "Bestellung " enthält Hinweise zur Middleware-Platzierung in der Anforderungsverarbeitungspipeline und unter welchen Bedingungen die Middleware die Anforderungsverarbeitung beenden kann. Wenn eine Middleware einen Kurzschluss in der Anforderungsverarbeitungspipeline verursacht und verhindert, dass Downstreammiddleware eine Anforderung verarbeitet, wird diese als Terminalmiddleware bezeichnet. Weitere Informationen zum Kurzschluss finden Sie unter " Erstellen einer Middleware-Pipeline mit WebApplication Abschnitt".

Middleware Beschreibung UI-Stapel Auftrag
Antiforgerie Bietet Unterstützung für Anti-request-forgery-Schutz All Nach Authentifizierung und Autorisierung, vor Endpunkten.
Authentifizierung Bietet Unterstützung für Authentifizierungen. All Vorher ist HttpContext.User erforderlich. Terminal für OAuth-Rückrufe.
Autorisierung Bietet Unterstützung für Authentifizierungen All Unmittelbar nach der Authentifizierungs-Middleware.
Cookierichtlinie Verfolgt die Zustimmung von Benutzern zum Speichern persönlicher Informationen und erzwingt die Mindeststandards für cookiefelder, z. B. secure und SameSite. All Vor der Middleware, die Cookies ausstellt. Beispiele: Authentifizierung, Sitzung, MVC (TempData).
CORS Konfiguriert die Ressourcenfreigabe zwischen verschiedenen Ursprüngen (Cross-Origin Resource Sharing, CORS). All Vor Middleware, die CORS verwenden. UseCors muss vor UseResponseCaching stehen. Weitere Informationen finden Sie unter Es ist nicht klar, dass UseCORS vor UseResponseCaching (dotnet/aspnetcore #23218) kommen muss.
die Seite mit Ausnahmen für Entwickler Generiert eine Seite mit Fehlerinformationen, die nur in der Development Umgebung verwendet werden sollen. All Vor Middleware, die Fehler generiert. Die Projektvorlagen registrieren diese Middleware automatisch als erste Middleware in der Pipeline, wenn die Umgebung Development ist.
Diagnose Mehrere separate Middlewares, die Entwicklern eine Ausnahmeseite, Ausnahmebehandlung, Statuscodeseiten und die Standardwebseite für neue Apps bereitstellen. All Vor Middleware, die Fehler generiert. Terminal für Ausnahmen oder zum Bereitstellen der Standardwebseite für neue Apps.
Weitergeleitete Header Leitet die Proxy-Header an die aktuelle Anforderung weiter. All Vor Middleware, die die aktualisierten Felder nutzt. Beispiele: Schema, Host, Client-IP, Methode.
Integritätsprüfung Überprüft die Integrität der ASP.NET Core-App und ihrer Abhängigkeiten, z. B. Überprüfung der Datenbankverfügbarkeit. All Abschließend, wenn eine Anforderung mit einem Integritätsprüfungs-Endpunkt übereinstimmt.
Headerweitergabe Überträgt HTTP-Header aus der eingehenden Anforderung zu ausgehenden HTTP-Clientanforderungen
All
HTTP-Protokollierung Hiermit werden HTTP-Anforderungen und -Antworten protokolliert. All Am Anfang der Middleware-Pipeline.
Außerkraftsetzung der HTTP-Methode Ermöglicht es eingehenden POST-Anforderungen, die Methode außer Kraft zu setzen. All Vor Middleware, die die aktualisierte Methode nutzt.
HTTPS-Umleitung Leitet alle HTTP-Anforderungen an HTTPS um. All Vor der Middleware, die die URL verarbeitet.
HTTP Strict Transport Security (HSTS) Middleware für erweiterte Sicherheit, die einen besonderen Antwortheader hinzufügt. All Bevor Antworten gesendet werden und nach dem Einsatz von Middleware, die Anfragen modifiziert. Beispiele: Forwarded Headers, URL-Rewriting.
MVC Verarbeitet Anfragen mit MVC und Razor Pages. RP/MVC Endpunkt, wenn eine Anforderung mit einer Route übereinstimmt.
OWIN Interoperabilität mit auf OWIN basierten Apps, Servern und Middleware. RP/MVC Terminal, wenn die OWIN Middleware die Anforderung vollständig verarbeitet.
Ausgabezwischenspeicherung Bietet Unterstützung für das Zwischenspeichern von Antworten basierend auf der Konfiguration. RP/MVC Vor Middleware, die eine Zwischenspeicherung erfordert. UseRouting, UseCors, UseAuthenticationund UseAuthorization muss vor UseOutputCache.
Antwort-Cache Bietet Unterstützung für das Zwischenspeichern von Antworten. Diese Middleware erfordert, dass die Clientbeteiligung funktioniert. Verwenden Sie Ausgabezwischenspeicherung für vollständige Serversteuerung. RP/MVC Vor Middleware, die eine Zwischenspeicherung erfordert. UseCors muss sich vor UseResponseCaching befinden. Das Zwischenspeichern von Antworten ist in der Regel für UI-Apps wie Razor Seiten nicht von Vorteil, da Browser in der Regel Anforderungsheader festlegen, die das Zwischenspeichern verhindern. Benutzeroberflächen-Apps profitieren von Ausgabezwischenspeicherung.
Anforderungsdekomprimierung Unterstützt das Dekomprimieren von Anfragen. All Vor der Middleware, die den Anfragekörper liest.
Antwortkomprimierung Bietet Unterstützung für das Komprimieren von Antworten. All Vor Middleware, die komprimiert werden muss.
Lokalisierungsanfrage Bietet Unterstützung für die Lokalisierung. All Vor der Lokalisierung vertraulicher Middleware. Muss nach der Routing-Middleware erscheinen, wenn RouteDataRequestCultureProvider verwendet wird.
Zeitüberschreitungen bei Anfragen Bietet Unterstützung beim Konfigurieren von Anforderungstimeouts, global und pro Endpunkt. All UseRequestTimeouts muss nach UseExceptionHandler, UseDeveloperExceptionPage und UseRouting kommen.
Endpunkt-Routing. Definiert Anforderungsrouten und schränkt diese ein. All Terminal für entsprechende Routen.
Wellness-Spa Verarbeitet alle Anforderungen ab diesem Punkt in der Middleware-Chain, indem die Standardseite für die Single-Page-Anwendung zurückgegeben wird. All Wird spät in der Pipeline angezeigt, sodass andere Middleware für die Bereitstellung statischer Dateien, z. B. MVC-Aktionen, Vorrang hat.
Sitzung Bietet Unterstützung für das Verwalten von Benutzersitzungen. RP/MVC Vor Middleware, die eine Session erfordern.
Statische Datei Bietet Unterstützung für das Verarbeiten statischer Dateien und das Durchsuchen des Verzeichnisses. All Abschließend, wenn eine Anforderung mit einer Datei übereinstimmt.
Umschreiben einer URL Bietet Unterstützung für das Umschreiben von URLs und das Umleiten von Anforderungen. All Vor der Middleware, die die URL verarbeitet.
W3C-Protokollierung Hiermit werden Serverzugriffsprotokolle im erweiterten W3C-Protokolldateiformat generiert. All Am Anfang der Middleware-Pipeline.
Blazor WebAssembly Debuggen Debuggt Blazor Web Apps, die clientseitiges Rendern (CSR) in den Chromium-Entwicklertools verwenden. BWA Am Anfang der Middleware-Pipeline.
WebSockets Aktiviert das WebSockets-Protokoll. All Vor Middleware, die WebSocket-Anfragen annehmen muss.

Zusätzliche Ressourcen