ASP.NET Core-Middleware

Hinweis

Dies ist nicht die neueste Version dieses Artikels. Informationen zum aktuellen Release finden Sie in der ASP.NET Core 8.0-Version dieses Artikels.

Von Rick Anderson und Steve Smith

Middleware ist Software, die zu einer Anwendungspipeline zusammengesetzt wird, um Anforderungen und Antworten zu verarbeiten. Jede Komponente kann Folgendes ausführen:

  • Entscheiden, ob die Anforderung an die nächste Komponente in der Pipeline übergeben werden soll.
  • Ausführen von Arbeiten, bevor oder nachdem die nächste Komponente in der Pipeline aufgerufen wird.

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

Anforderungsdelegaten werden mit den Erweiterungsmethoden Run, Map und Use konfiguriert. Ein einzelner Anforderungsdelegat kann inline als anonyme Methode angegeben werden (sogenannte Inline-Middleware), oder er kann in einer wiederverwendbaren Klasse definiert werden. Diese wiederverwendbaren Klassen und anonymen Inline-Methoden sind Middleware bzw. Middlewarekomponenten. Jede Middlewarekomponente in der Anforderungspipeline ist für das Aufrufen der jeweils nächsten Komponente in der Pipeline oder, wenn nötig, für das Kurzschließen der Pipeline zuständig. Wenn eine Middleware einen Kurzschluss verursacht, wird diese als Terminalmiddleware bezeichnet, da sie verhindert, dass weitere Middleware die Anforderung verarbeiten kann.

Unter Migrieren von HTTP-Handlern und -Modulen zu ASP.NET Core-Middleware wird der Unterschied zwischen Anforderungspipelines in ASP.NET Core und ASP.NET 4.x erläutert, und es werden zusätzliche Beispiele für Middleware bereitgestellt.

Middleware-Codeanalyse

ASP.NET Core enthält zahlreiche Compilerplattform-Analysetools zum Überprüfen der Qualität des Anwendungscodes. Weitere Informationen finden Sie unter Codeanalyse 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.

Request processing pattern showing a request arriving, processing through three middlewares, and the response leaving the app. Each middleware runs its logic and hands off the request to the next middleware at the next() statement. After the third middleware processes the request, the request passes back through the prior two middlewares in reverse order for additional processing after their next() statements before leaving the app as a response to the client.

Jeder Delegat kann Vorgänge vor und nach dem nächsten Delegaten ausführen. Die Ausnahmebehandlungsdelegaten müssen am Anfang der Pipeline aufgerufen werden, sodass sie Ausnahmen abfangen können, die zu einem späteren Zeitpunkt in der Pipeline ausgelöst werden.

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.

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

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

app.Run();

Mit Use können Sie mehrere Anforderungedelegate 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 den Parameter nextnicht aufrufen. Normalerweise können Sie Aktionen sowohl vor als auch nach dem next-Delegaten durchführen. Dies wird in folgendem Beispiel veranschaulicht:

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

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

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

app.Run();

Kurzschluss der Anforderungspipeline

Wenn ein keine Anforderung an den nächsten Delegaten übergibt, wird dies als Kurzschluss der Anforderungspipeline bezeichnet. Das Kurzschließen ist oft sinnvoll, da es unnötige Arbeit verhindert. Die Middleware für statische Dateien kann beispielsweise als Terminalmiddleware fungieren, indem sie eine Anforderung für eine statische Datei zurückgibt und den Rest der Pipeline kurzschließt. 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. An HttpResponse vorgenommene Änderungen 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, wenn Sie z.B. mehr als das genannte Content-Length-Objekt 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, der angibt, ob Header gesendet wurden oder ob in den Text geschrieben wurde.

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

Run-Delegaten

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

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

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

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

app.Run();

Wenn Sie möchten, dass Codekommentare in anderen Sprachen als Englisch angezeigt werden, informieren Sie uns in diesem GitHub-Issue.

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.

App bevorzugen: Verwenden einer Überladung, die die Übergabe des Kontexts erfordert

Die app.Use-Erweiterungsmethode ohne Zuordnung:

  • Erfordert die Übergabe des Kontexts an next
  • Speichert zwei interne Zuordnungen pro Anforderung, die bei Verwendung der anderen Überladung erforderlich sind

Weitere Informationen finden Sie in diesem GitHub-Issue.

Middlewarereihenfolge

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 neu angeordnet oder neue benutzerdefinierte Middleware nach Bedarf eingefügt wird.

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 Routing-Middleware im vorherigen Diagramm ist im Anschluss an statische Dateien dargestellt. Dies ist die Reihenfolge, die die Projektvorlagen implementieren, indem sie explizit app.UseRouting aufrufen. Wenn Sie nicht app.UseRouting aufrufen, wird die Routing-Middleware standardmäßig am Anfang der Pipeline ausgeführt. Weitere Informationen finden Sie unter Routing.

ASP.NET Core filter pipeline

Die Reihenfolge, in der Middlewarekomponenten in der Program.cs-Datei 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.

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

Für den Code oben gilt:

  • Middleware, die beim Erstellen einer neuen Web-App mit einzelnen Benutzerkonten nicht hinzugefügt wird, wird auskommentiert.
  • 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 aufgrund 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 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();

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

  1. Ausnahme-/Fehlerbehandlung
    • Bei Ausführung der App in der Entwicklungsumgebung:
    • Bei Ausführung der App in der Produktionsumgebung:
      • Middleware für Ausnahmehandler (UseExceptionHandler) fängt Ausnahmen ab, die in den folgenden Middlewarekomponenten ausgelöst werden.
      • HTTP Strict Transport Security Protocol-Middleware (HSTS) (UseHsts) fügt den Strict-Transport-Security-Header hinzu.
  2. Middleware zur HTTPS-Umleitung (UseHttpsRedirection) leitet HTTP-Anforderungen an HTTPS um.
  3. Middleware für statische Dateien (UseStaticFiles) gibt statische Dateien zurück und umgeht die weitere Anforderungsverarbeitung.
  4. Middleware für Cookierichtlinien (UseCookiePolicy) sorgt dafür, dass die App die Anforderungen der europäischen Datenschutzgrundverordnung (DSGVO) erfüllt.
  5. Routingmiddleware (UseRouting) zum Weiterleiten von Anforderungen.
  6. Middleware für die Authentifizierung (UseAuthentication) versucht, den Benutzer zu authentifizieren, bevor der Zugriff auf sichere Ressourcen zugelassen wird.
  7. Autorisierungsmiddleware (UseAuthorization) wird zum Autorisieren des Zugriffs auf sichere Ressourcen eines Benutzers verwendet.
  8. Middleware für Sitzungen (UseSession) richtet einen Sitzungsstatus ein und erhält diesen aufrecht. Wenn die App den Sitzungsstatus verwendet, rufen Sie die Middleware für Sitzungen nach der Middleware für Cookierichtlinien 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. Aus diesem Grund fängt die Middleware für den Ausnahmehandler 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 Middleware für statische Dateien stellt keine Autorisierungsüberprüfungen bereit. Alle Dateien, die von Middleware für statische Dateien unterstützt 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 Middleware für statische Dateien verarbeitet wird, wird sie an die Authentifizierungsmiddleware (UseAuthentication) übergeben, welche die Authentifizierung durchführt. Die Authentifizierung schließt nicht authentifizierte Anforderungen nicht kurz. Auch wenn die Authentifizierungsmiddleware Anforderungen authentifiziert, erfolgt die Autorisierung (und Ablehnung) erst dann, wenn MVC eine spezifische Razor Page oder einen MVC-Controller und eine Aktion ausgewählt hat.

Im folgenden Beispiel wird eine Middlewarereihenfolge veranschaulicht, bei der Anforderungen statischer Dateien von der Middleware für statische Dateien vor der Middleware für die Antwortkomprimierung verarbeitet werden. Die statischen Dateien werden bei dieser Middlewarereihenfolge 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();

Informationen zu Einzelseitenanwendungen finden Sie unter Übersicht über Single Page Apps (SPAs) in ASP.NET Core.

Reihenfolge von UseCors und UseStaticFiles

Die Reihenfolge der Aufrufe von UseCors und UseStaticFiles hängt von der App ab. Weitere Informationen finden Sie unter Reihenfolge von UseCors und UseStaticFiles

Middleware für weitergeleitete Header: Auftrag

Middleware für weitergeleitete Header muss vor anderen Middlewarekomponenten ausgeführt werden. Mit dieser Reihenfolge wird sichergestellt, dass die auf Informationen von weitergeleiteten Headern basierende Middleware die zu verarbeitenden Headerwerte nutzen kann. Unter Middleware für weitergeleitete Header: Auftrag finden Sie Informationen zum Ausführen der Middleware für weitergeleitete Header nach der diagnostischen Middleware und der Middleware für die Fehlerbehandlung.

Branchen der Middlewarepipeline

Map-Erweiterungen werden als Konvention zum Branchen der Pipeline verwendet. Map brancht die Anforderungspipeline auf Grundlage von Übereinstimmungen des angegebenen Anforderungspfads. Wenn der Anforderungspfad mit dem angegebenen Pfad beginnt, wird der Branch ausgeführt.

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

app.Map("/map1", HandleMapTest1);

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

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

app.Run();

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

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

In der folgenden Tabelle sind die Anforderungen und Antworten von http://localhost:1234 mit dem oben stehenden Code aufgelistet.

Anforderung Antwort
localhost:1234 Hello from non-Map delegate.
localhost:1234/map1 Map Test 1
localhost:1234/map2 Map Test 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 das Schachteln, wie z.B. in folgendem Code:

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 zuordnen:

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

app.Map("/map1/seg1", HandleMultiSeg);

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

app.Run();

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

MapWhen brancht die Anforderungspipeline auf Grundlage des Ergebnisses 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 der Abfragezeichenfolgenvariablen branch zu ermitteln:

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 non-Map delegate.");
});

app.Run();

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 von http://localhost:1234 mit dem oben stehenden Code aufgelistet:

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

UseWhen brancht auch die Anforderungspipeline auf Grundlage des Ergebnisses 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:

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

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

Im vorherigen Beispiel wird die Antwort Hello from non-Map delegate. für alle Anforderungen ausgegeben. Wenn die Anforderung eine Abfragezeichenfolgevariable branch enthält, wird der Wert der Pipeline protokolliert, bevor eine neue Verbindung hergestellt wird.

Integrierte Middleware

Die folgenden Middlewarekomponenten sind im Lieferumfang von ASP.NET Core enthalten. Die Spalte Reihenfolge enthält Hinweise zur Platzierung der Middleware in der Pipeline, die die Anforderung verarbeitet, und zu den Bedingungen, unter denen die Middleware die Anforderungsverarbeitung möglicherweise beendet. Wenn eine Middleware einen Kurzschluss in der Anforderungsverarbeitungspipeline verursacht und verhindert, dass Downstreammiddleware eine Anforderung verarbeitet, wird diese als Terminalmiddleware bezeichnet. Weitere Informationen zu Kurzschlüssen finden Sie im Abschnitt Erstellen einer Middlewarepipeline mit WebApplication.

Middleware Beschreibung Auftrag
Authentifizierung Bietet Unterstützung für Authentifizierungen. Bevor HttpContext.User erforderlich ist. Terminal für OAuth-Rückrufe.
Autorisierung Bietet Unterstützung für Authentifizierungen Direkt nach der Authentifizierungsmiddleware
Cookierichtlinie Verfolgt die Zustimmung von Benutzern zum Speichern persönlicher Informationen und erzwingt die Mindeststandards für cookiefelder, z. B. secure und SameSite. Befindet sich vor der Middleware, die cookies ausstellt. Beispiele: Authentifizierung, Sitzung, MVC (TempData).
CORS Konfiguriert die Ressourcenfreigabe zwischen verschiedenen Ursprüngen (Cross-Origin Resource Sharing, CORS). Vor Komponenten, die CORS verwenden. UseCors muss sich derzeit aufgrund dieses Fehlers vor UseResponseCaching befinden.
DeveloperExceptionPage Diese generiert eine Seite mit Fehlerinformationen, die nur für die Verwendung in der Entwicklungsumgebung vorgesehen sind. Vor Komponenten, die Fehler erzeugen. Die Projektvorlagen registrieren diese Middleware automatisch als erste Middleware in der Pipeline, wenn die Umgebung Entwicklung ist.
Diagnose Mehrere separate Middlewares, die Entwicklern eine Ausnahmeseite, Ausnahmebehandlung, Statuscodeseiten und die Standardwebseite für neue Apps bereitstellen. Vor Komponenten, die Fehler erzeugen. Terminal für Ausnahmen oder zum Bereitstellen der Standardwebseite für neue Apps.
Weitergeleitete Header Leitet Proxyheader an die aktuelle Anforderung weiter. Vor Komponenten, die die aktualisierten Felder nutzen. 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. 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
HTTP-Protokollierung Hiermit werden HTTP-Anforderungen und -Antworten protokolliert. Am Anfang der Middlewarepipeline.
Außerkraftsetzung der HTTP-Methode Ermöglicht es eingehenden POST-Anforderungen, die Methode außer Kraft zu setzen. Vor Komponenten, die die aktualisierte Methode nutzen.
HTTPS-Umleitung Leitet alle HTTP-Anforderungen an HTTPS um. Vor Komponenten, die die URL nutzen.
HTTP Strict Transport Security (HSTS) Middleware für erweiterte Sicherheit, die einen besonderen Antwortheader hinzufügt. Bevor Antworten gesendet werden und nach Komponenten, die Anforderungen ändern. Beispiele: weitergeleitete Header, URL-Umschreibung.
MVC Verarbeitet Anforderungen mit MVC/Razor Pages. Abschließend, wenn eine Anforderung mit einer Route übereinstimmt.
OWIN Interoperabilität mit auf OWIN basierten Apps, Servern und Middleware. Abschließend, wenn die OWIN-Middleware die Anforderung vollständig verarbeitet.
Ausgabezwischenspeicherung Bietet Unterstützung für das Zwischenspeichern von Antworten basierend auf der Konfiguration. Vor Komponenten, für die das Zwischenspeichern erforderlich ist. UseRouting muss sich vor UseOutputCaching befinden. UseCORS muss sich vor UseOutputCaching befinden.
Zwischenspeichern von Antworten Bietet Unterstützung für das Zwischenspeichern von Antworten. Dies erfordert die Teilnahme des Clients, um zu funktionieren. Verwenden Sie Ausgabezwischenspeicherung für vollständige Serversteuerung. Vor Komponenten, für die das Zwischenspeichern erforderlich ist. UseCORS muss sich vor UseResponseCaching befinden. Ist in der Regel nicht vorteilhaft für Benutzeroberflächen-Apps wie Razor Pages, da Browser im Allgemeinen Anforderungsheader festlegen, die das Zwischenspeichern verhindern. Benutzeroberflächen-Apps profitieren von Ausgabezwischenspeicherung.
Anforderungsdekomprimierung Bietet Unterstützung für das Dekomprimieren von Anforderungen. Vor Komponenten, die den Anforderungstext lesen.
Antwortkomprimierung Bietet Unterstützung für das Komprimieren von Antworten. Vor Komponenten, für die das Komprimieren erforderlich ist.
Lokalisierung von Anforderungen Bietet Unterstützung für die Lokalisierung. Vor der Lokalisierung vertraulicher Komponenten. Muss hinter Routing-Middleware stehen, wenn RouteDataRequestCultureProvider verwendet wird.
Anforderungstimeouts Bietet Unterstützung beim Konfigurieren von Anforderungstimeouts, global und pro Endpunkt. UseRequestTimeouts muss nach UseExceptionHandler, UseDeveloperExceptionPage und UseRouting kommen.
Endpunktrouting Definiert Anforderungsrouten und schränkt diese ein. Terminal für entsprechende Routen.
SPA Verarbeitet alle Anforderungen ab diesem Punkt in der Middlewarekette, indem die Standardseite für die Single-Page-Anwendung zurückgegeben wird. Kommt spät in der Kette, sodass andere Middleware, z. B. die zum Bereitstellen von statischen Dateien oder MVC-Aktionen, Vorrang hat.
Sitzung Bietet Unterstützung für das Verwalten von Benutzersitzungen. Vor Komponenten, für die Sitzungen erforderlich sind.
Statische Dateien Bietet Unterstützung für das Verarbeiten statischer Dateien und das Durchsuchen des Verzeichnisses. 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. Vor Komponenten, die die URL nutzen.
W3CLogging Hiermit werden Serverzugriffsprotokolle im erweiterten W3C-Protokolldateiformat generiert. Am Anfang der Middlewarepipeline.
WebSockets Aktiviert das WebSockets-Protokoll. Vor Komponenten, die WebSocket-Anforderungen annehmen müssen.

Zusätzliche Ressourcen

Von Rick Anderson und Steve Smith

Middleware ist Software, die zu einer Anwendungspipeline zusammengesetzt wird, um Anforderungen und Antworten zu verarbeiten. Jede Komponente kann Folgendes ausführen:

  • Entscheiden, ob die Anforderung an die nächste Komponente in der Pipeline übergeben werden soll.
  • Ausführen von Arbeiten, bevor oder nachdem die nächste Komponente in der Pipeline aufgerufen wird.

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

Anforderungsdelegaten werden mit den Erweiterungsmethoden Run, Map und Use konfiguriert. Ein einzelner Anforderungsdelegat kann inline als anonyme Methode angegeben werden (sogenannte Inline-Middleware), oder er kann in einer wiederverwendbaren Klasse definiert werden. Diese wiederverwendbaren Klassen und anonymen Inline-Methoden sind Middleware bzw. Middlewarekomponenten. Jede Middlewarekomponente in der Anforderungspipeline ist für das Aufrufen der jeweils nächsten Komponente in der Pipeline oder, wenn nötig, für das Kurzschließen der Pipeline zuständig. Wenn eine Middleware einen Kurzschluss verursacht, wird diese als Terminalmiddleware bezeichnet, da sie verhindert, dass weitere Middleware die Anforderung verarbeiten kann.

Unter Migrieren von HTTP-Handlern und -Modulen zu ASP.NET Core-Middleware wird der Unterschied zwischen Anforderungspipelines in ASP.NET Core und ASP.NET 4.x erläutert, und es werden zusätzliche Beispiele für Middleware bereitgestellt.

Middleware-Codeanalyse

ASP.NET Core enthält zahlreiche Compilerplattform-Analysetools zum Überprüfen der Qualität des Anwendungscodes. Weitere Informationen finden Sie unter Codeanalyse 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.

Request processing pattern showing a request arriving, processing through three middlewares, and the response leaving the app. Each middleware runs its logic and hands off the request to the next middleware at the next() statement. After the third middleware processes the request, the request passes back through the prior two middlewares in reverse order for additional processing after their next() statements before leaving the app as a response to the client.

Jeder Delegat kann Vorgänge vor und nach dem nächsten Delegaten ausführen. Die Ausnahmebehandlungsdelegaten müssen am Anfang der Pipeline aufgerufen werden, sodass sie Ausnahmen abfangen können, die zu einem späteren Zeitpunkt in der Pipeline ausgelöst werden.

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.

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

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

app.Run();

Mit Use können Sie mehrere Anforderungedelegate 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 den Parameter nextnicht aufrufen. Normalerweise können Sie Aktionen sowohl vor als auch nach dem next-Delegaten durchführen. Dies wird in folgendem Beispiel veranschaulicht:

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

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

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

app.Run();

Wenn ein keine Anforderung an den nächsten Delegaten übergibt, wird dies als Kurzschluss der Anforderungspipeline bezeichnet. Das Kurzschließen ist oft sinnvoll, da es unnötige Arbeit verhindert. Die Middleware für statische Dateien kann beispielsweise als Terminalmiddleware fungieren, indem sie eine Anforderung für eine statische Datei zurückgibt und den Rest der Pipeline kurzschließt. 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. An HttpResponse vorgenommene Änderungen 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, wenn Sie z.B. mehr als das genannte Content-Length-Objekt 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, der angibt, ob Header gesendet wurden oder ob in den Text geschrieben wurde.

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

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

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

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

app.Run();

Wenn Sie möchten, dass Codekommentare in anderen Sprachen als Englisch angezeigt werden, informieren Sie uns in diesem GitHub-Issue.

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.

App bevorzugen: Verwenden einer Überladung, die die Übergabe des Kontexts erfordert

Die app.Use-Erweiterungsmethode ohne Zuordnung:

  • Erfordert die Übergabe des Kontexts an next
  • Speichert zwei interne Zuordnungen pro Anforderung, die bei Verwendung der anderen Überladung erforderlich sind

Weitere Informationen finden Sie in diesem GitHub-Issue.

Middlewarereihenfolge

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 neu angeordnet oder neue benutzerdefinierte Middleware nach Bedarf eingefügt wird.

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 Routing-Middleware im vorherigen Diagramm ist im Anschluss an statische Dateien dargestellt. Dies ist die Reihenfolge, die die Projektvorlagen implementieren, indem sie explizit app.UseRouting aufrufen. Wenn Sie nicht app.UseRouting aufrufen, wird die Routing-Middleware standardmäßig am Anfang der Pipeline ausgeführt. Weitere Informationen finden Sie unter Routing.

ASP.NET Core filter pipeline

Die Reihenfolge, in der Middlewarekomponenten in der Program.cs-Datei 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.

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

Für den Code oben gilt:

  • Middleware, die beim Erstellen einer neuen Web-App mit einzelnen Benutzerkonten nicht hinzugefügt wird, wird auskommentiert.
  • 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 aufgrund 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 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();

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

  1. Ausnahme-/Fehlerbehandlung
    • Bei Ausführung der App in der Entwicklungsumgebung:
    • Bei Ausführung der App in der Produktionsumgebung:
      • Middleware für Ausnahmehandler (UseExceptionHandler) fängt Ausnahmen ab, die in den folgenden Middlewarekomponenten ausgelöst werden.
      • HTTP Strict Transport Security Protocol-Middleware (HSTS) (UseHsts) fügt den Strict-Transport-Security-Header hinzu.
  2. Middleware zur HTTPS-Umleitung (UseHttpsRedirection) leitet HTTP-Anforderungen an HTTPS um.
  3. Middleware für statische Dateien (UseStaticFiles) gibt statische Dateien zurück und umgeht die weitere Anforderungsverarbeitung.
  4. Middleware für Cookierichtlinien (UseCookiePolicy) sorgt dafür, dass die App die Anforderungen der europäischen Datenschutzgrundverordnung (DSGVO) erfüllt.
  5. Routingmiddleware (UseRouting) zum Weiterleiten von Anforderungen.
  6. Middleware für die Authentifizierung (UseAuthentication) versucht, den Benutzer zu authentifizieren, bevor der Zugriff auf sichere Ressourcen zugelassen wird.
  7. Autorisierungsmiddleware (UseAuthorization) wird zum Autorisieren des Zugriffs auf sichere Ressourcen eines Benutzers verwendet.
  8. Middleware für Sitzungen (UseSession) richtet einen Sitzungsstatus ein und erhält diesen aufrecht. Wenn die App den Sitzungsstatus verwendet, rufen Sie die Middleware für Sitzungen nach der Middleware für Cookierichtlinien 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. Aus diesem Grund fängt die Middleware für den Ausnahmehandler 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 Middleware für statische Dateien stellt keine Autorisierungsüberprüfungen bereit. Alle Dateien, die von Middleware für statische Dateien unterstützt 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 Middleware für statische Dateien verarbeitet wird, wird sie an die Authentifizierungsmiddleware (UseAuthentication) übergeben, welche die Authentifizierung durchführt. Die Authentifizierung schließt nicht authentifizierte Anforderungen nicht kurz. Auch wenn die Authentifizierungsmiddleware Anforderungen authentifiziert, erfolgt die Autorisierung (und Ablehnung) erst dann, wenn MVC eine spezifische Razor Page oder einen MVC-Controller und eine Aktion ausgewählt hat.

Im folgenden Beispiel wird eine Middlewarereihenfolge veranschaulicht, bei der Anforderungen statischer Dateien von der Middleware für statische Dateien vor der Middleware für die Antwortkomprimierung verarbeitet werden. Die statischen Dateien werden bei dieser Middlewarereihenfolge 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();

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

Reihenfolge von UseCors und UseStaticFiles

Die Reihenfolge der Aufrufe von UseCors und UseStaticFiles hängt von der App ab. Weitere Informationen finden Sie unter Reihenfolge von UseCors und UseStaticFiles

Middleware für weitergeleitete Header: Auftrag

Middleware für weitergeleitete Header muss vor anderen Middlewarekomponenten ausgeführt werden. Mit dieser Reihenfolge wird sichergestellt, dass die auf Informationen von weitergeleiteten Headern basierende Middleware die zu verarbeitenden Headerwerte nutzen kann. Unter Middleware für weitergeleitete Header: Auftrag finden Sie Informationen zum Ausführen der Middleware für weitergeleitete Header nach der diagnostischen Middleware und der Middleware für die Fehlerbehandlung.

Branchen der Middlewarepipeline

Map-Erweiterungen werden als Konvention zum Branchen der Pipeline verwendet. Map brancht die Anforderungspipeline auf Grundlage von Übereinstimmungen des angegebenen Anforderungspfads. Wenn der Anforderungspfad mit dem angegebenen Pfad beginnt, wird der Branch ausgeführt.

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

app.Map("/map1", HandleMapTest1);

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

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

app.Run();

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

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

In der folgenden Tabelle sind die Anforderungen und Antworten von http://localhost:1234 mit dem oben stehenden Code aufgelistet.

Anforderung Antwort
localhost:1234 Hello from non-Map delegate.
localhost:1234/map1 Map Test 1
localhost:1234/map2 Map Test 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 das Schachteln, wie z.B. in folgendem Code:

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 zuordnen:

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

app.Map("/map1/seg1", HandleMultiSeg);

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

app.Run();

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

MapWhen brancht die Anforderungspipeline auf Grundlage des Ergebnisses 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 der Abfragezeichenfolgenvariablen branch zu ermitteln:

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 non-Map delegate.");
});

app.Run();

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 von http://localhost:1234 mit dem oben stehenden Code aufgelistet:

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

UseWhen brancht auch die Anforderungspipeline auf Grundlage des Ergebnisses 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:

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

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

Im vorherigen Beispiel wird die Antwort Hello from non-Map delegate. für alle Anforderungen ausgegeben. Wenn die Anforderung eine Abfragezeichenfolgevariable branch enthält, wird der Wert der Pipeline protokolliert, bevor eine neue Verbindung hergestellt wird.

Integrierte Middleware

Die folgenden Middlewarekomponenten sind im Lieferumfang von ASP.NET Core enthalten. Die Spalte Reihenfolge enthält Hinweise zur Platzierung der Middleware in der Pipeline, die die Anforderung verarbeitet, und zu den Bedingungen, unter denen die Middleware die Anforderungsverarbeitung möglicherweise beendet. Wenn eine Middleware einen Kurzschluss in der Anforderungsverarbeitungspipeline verursacht und verhindert, dass Downstreammiddleware eine Anforderung verarbeitet, wird diese als Terminalmiddleware bezeichnet. Weitere Informationen zu Kurzschlüssen finden Sie im Abschnitt Erstellen einer Middlewarepipeline mit WebApplication.

Middleware Beschreibung Auftrag
Authentifizierung Bietet Unterstützung für Authentifizierungen. Bevor HttpContext.User erforderlich ist. Terminal für OAuth-Rückrufe.
Autorisierung Bietet Unterstützung für Authentifizierungen Direkt nach der Authentifizierungsmiddleware
Cookierichtlinie Verfolgt die Zustimmung von Benutzern zum Speichern persönlicher Informationen und erzwingt die Mindeststandards für cookiefelder, z. B. secure und SameSite. Befindet sich vor der Middleware, die cookies ausstellt. Beispiele: Authentifizierung, Sitzung, MVC (TempData).
CORS Konfiguriert die Ressourcenfreigabe zwischen verschiedenen Ursprüngen (Cross-Origin Resource Sharing, CORS). Vor Komponenten, die CORS verwenden. UseCors muss sich derzeit aufgrund dieses Fehlers vor UseResponseCaching befinden.
DeveloperExceptionPage Diese generiert eine Seite mit Fehlerinformationen, die nur für die Verwendung in der Entwicklungsumgebung vorgesehen sind. Vor Komponenten, die Fehler erzeugen. Die Projektvorlagen registrieren diese Middleware automatisch als erste Middleware in der Pipeline, wenn die Umgebung Entwicklung ist.
Diagnose Mehrere separate Middlewares, die Entwicklern eine Ausnahmeseite, Ausnahmebehandlung, Statuscodeseiten und die Standardwebseite für neue Apps bereitstellen. Vor Komponenten, die Fehler erzeugen. Terminal für Ausnahmen oder zum Bereitstellen der Standardwebseite für neue Apps.
Weitergeleitete Header Leitet Proxyheader an die aktuelle Anforderung weiter. Vor Komponenten, die die aktualisierten Felder nutzen. 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. 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
HTTP-Protokollierung Hiermit werden HTTP-Anforderungen und -Antworten protokolliert. Am Anfang der Middlewarepipeline.
Außerkraftsetzung der HTTP-Methode Ermöglicht es eingehenden POST-Anforderungen, die Methode außer Kraft zu setzen. Vor Komponenten, die die aktualisierte Methode nutzen.
HTTPS-Umleitung Leitet alle HTTP-Anforderungen an HTTPS um. Vor Komponenten, die die URL nutzen.
HTTP Strict Transport Security (HSTS) Middleware für erweiterte Sicherheit, die einen besonderen Antwortheader hinzufügt. Bevor Antworten gesendet werden und nach Komponenten, die Anforderungen ändern. Beispiele: weitergeleitete Header, URL-Umschreibung.
MVC Verarbeitet Anforderungen mit MVC/Razor Pages. Abschließend, wenn eine Anforderung mit einer Route übereinstimmt.
OWIN Interoperabilität mit auf OWIN basierten Apps, Servern und Middleware. Abschließend, wenn die OWIN-Middleware die Anforderung vollständig verarbeitet.
Ausgabezwischenspeicherung Bietet Unterstützung für das Zwischenspeichern von Antworten basierend auf der Konfiguration. Vor Komponenten, für die das Zwischenspeichern erforderlich ist. UseRouting muss sich vor UseOutputCaching befinden. UseCORS muss sich vor UseOutputCaching befinden.
Zwischenspeichern von Antworten Bietet Unterstützung für das Zwischenspeichern von Antworten. Dies erfordert die Teilnahme des Clients, um zu funktionieren. Verwenden Sie Ausgabezwischenspeicherung für vollständige Serversteuerung. Vor Komponenten, für die das Zwischenspeichern erforderlich ist. UseCORS muss sich vor UseResponseCaching befinden. Ist in der Regel nicht vorteilhaft für Benutzeroberflächen-Apps wie Razor Pages, da Browser im Allgemeinen Anforderungsheader festlegen, die das Zwischenspeichern verhindern. Benutzeroberflächen-Apps profitieren von Ausgabezwischenspeicherung.
Anforderungsdekomprimierung Bietet Unterstützung für das Dekomprimieren von Anforderungen. Vor Komponenten, die den Anforderungstext lesen.
Antwortkomprimierung Bietet Unterstützung für das Komprimieren von Antworten. Vor Komponenten, für die das Komprimieren erforderlich ist.
Lokalisierung von Anforderungen Bietet Unterstützung für die Lokalisierung. Vor der Lokalisierung vertraulicher Komponenten. Muss hinter Routing-Middleware stehen, wenn RouteDataRequestCultureProvider verwendet wird.
Endpunktrouting Definiert Anforderungsrouten und schränkt diese ein. Terminal für entsprechende Routen.
SPA Verarbeitet alle Anforderungen ab diesem Punkt in der Middlewarekette, indem die Standardseite für die Single-Page-Anwendung zurückgegeben wird. Kommt spät in der Kette, sodass andere Middleware, z. B. die zum Bereitstellen von statischen Dateien oder MVC-Aktionen, Vorrang hat.
Sitzung Bietet Unterstützung für das Verwalten von Benutzersitzungen. Vor Komponenten, für die Sitzungen erforderlich sind.
Statische Dateien Bietet Unterstützung für das Verarbeiten statischer Dateien und das Durchsuchen des Verzeichnisses. 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. Vor Komponenten, die die URL nutzen.
W3CLogging Hiermit werden Serverzugriffsprotokolle im erweiterten W3C-Protokolldateiformat generiert. Am Anfang der Middlewarepipeline.
WebSockets Aktiviert das WebSockets-Protokoll. Vor Komponenten, die WebSocket-Anforderungen annehmen müssen.

Zusätzliche Ressourcen

Von Rick Anderson und Steve Smith

Middleware ist Software, die zu einer Anwendungspipeline zusammengesetzt wird, um Anforderungen und Antworten zu verarbeiten. Jede Komponente kann Folgendes ausführen:

  • Entscheiden, ob die Anforderung an die nächste Komponente in der Pipeline übergeben werden soll.
  • Ausführen von Arbeiten, bevor oder nachdem die nächste Komponente in der Pipeline aufgerufen wird.

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

Anforderungsdelegaten werden mit den Erweiterungsmethoden Run, Map und Use konfiguriert. Ein einzelner Anforderungsdelegat kann inline als anonyme Methode angegeben werden (sogenannte Inline-Middleware), oder er kann in einer wiederverwendbaren Klasse definiert werden. Diese wiederverwendbaren Klassen und anonymen Inline-Methoden sind Middleware bzw. Middlewarekomponenten. Jede Middlewarekomponente in der Anforderungspipeline ist für das Aufrufen der jeweils nächsten Komponente in der Pipeline oder, wenn nötig, für das Kurzschließen der Pipeline zuständig. Wenn eine Middleware einen Kurzschluss verursacht, wird diese als Terminalmiddleware bezeichnet, da sie verhindert, dass weitere Middleware die Anforderung verarbeiten kann.

Unter Migrieren von HTTP-Handlern und -Modulen zu ASP.NET Core-Middleware wird der Unterschied zwischen Anforderungspipelines in ASP.NET Core und ASP.NET 4.x erläutert, und es werden zusätzliche Beispiele für Middleware bereitgestellt.

Middleware-Codeanalyse

ASP.NET Core enthält zahlreiche Compilerplattform-Analysetools zum Überprüfen der Qualität des Anwendungscodes. Weitere Informationen finden Sie unter Codeanalyse 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.

Request processing pattern showing a request arriving, processing through three middlewares, and the response leaving the app. Each middleware runs its logic and hands off the request to the next middleware at the next() statement. After the third middleware processes the request, the request passes back through the prior two middlewares in reverse order for additional processing after their next() statements before leaving the app as a response to the client.

Jeder Delegat kann Vorgänge vor und nach dem nächsten Delegaten ausführen. Die Ausnahmebehandlungsdelegaten müssen am Anfang der Pipeline aufgerufen werden, sodass sie Ausnahmen abfangen können, die zu einem späteren Zeitpunkt in der Pipeline ausgelöst werden.

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.

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

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

app.Run();

Mit Use können Sie mehrere Anforderungedelegate 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 den Parameter nextnicht aufrufen. Normalerweise können Sie Aktionen sowohl vor als auch nach dem next-Delegaten durchführen. Dies wird in folgendem Beispiel veranschaulicht:

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

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

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

app.Run();

Wenn ein keine Anforderung an den nächsten Delegaten übergibt, wird dies als Kurzschluss der Anforderungspipeline bezeichnet. Das Kurzschließen ist oft sinnvoll, da es unnötige Arbeit verhindert. Die Middleware für statische Dateien kann beispielsweise als Terminalmiddleware fungieren, indem sie eine Anforderung für eine statische Datei zurückgibt und den Rest der Pipeline kurzschließt. 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. An HttpResponse vorgenommene Änderungen 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, wenn Sie z.B. mehr als das genannte Content-Length-Objekt 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, der angibt, ob Header gesendet wurden oder ob in den Text geschrieben wurde.

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

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

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

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

app.Run();

Wenn Sie möchten, dass Codekommentare in anderen Sprachen als Englisch angezeigt werden, informieren Sie uns in diesem GitHub-Issue.

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.

App bevorzugen: Verwenden einer Überladung, die die Übergabe des Kontexts erfordert

Die app.Use-Erweiterungsmethode ohne Zuordnung:

  • Erfordert die Übergabe des Kontexts an next
  • Speichert zwei interne Zuordnungen pro Anforderung, die bei Verwendung der anderen Überladung erforderlich sind

Weitere Informationen finden Sie in diesem GitHub-Issue.

Middlewarereihenfolge

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 neu angeordnet oder neue benutzerdefinierte Middleware nach Bedarf eingefügt wird.

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 Routing-Middleware im vorherigen Diagramm ist im Anschluss an statische Dateien dargestellt. Dies ist die Reihenfolge, die die Projektvorlagen implementieren, indem sie explizit app.UseRouting aufrufen. Wenn Sie nicht app.UseRouting aufrufen, wird die Routing-Middleware standardmäßig am Anfang der Pipeline ausgeführt. Weitere Informationen finden Sie unter Routing.

ASP.NET Core filter pipeline

Die Reihenfolge, in der Middlewarekomponenten in der Program.cs-Datei 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.

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

Für den Code oben gilt:

  • Middleware, die beim Erstellen einer neuen Web-App mit einzelnen Benutzerkonten nicht hinzugefügt wird, wird auskommentiert.
  • 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 aufgrund 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 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();

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

  1. Ausnahme-/Fehlerbehandlung
    • Bei Ausführung der App in der Entwicklungsumgebung:
    • Bei Ausführung der App in der Produktionsumgebung:
      • Middleware für Ausnahmehandler (UseExceptionHandler) fängt Ausnahmen ab, die in den folgenden Middlewarekomponenten ausgelöst werden.
      • HTTP Strict Transport Security Protocol-Middleware (HSTS) (UseHsts) fügt den Strict-Transport-Security-Header hinzu.
  2. Middleware zur HTTPS-Umleitung (UseHttpsRedirection) leitet HTTP-Anforderungen an HTTPS um.
  3. Middleware für statische Dateien (UseStaticFiles) gibt statische Dateien zurück und umgeht die weitere Anforderungsverarbeitung.
  4. Middleware für Cookierichtlinien (UseCookiePolicy) sorgt dafür, dass die App die Anforderungen der europäischen Datenschutzgrundverordnung (DSGVO) erfüllt.
  5. Routingmiddleware (UseRouting) zum Weiterleiten von Anforderungen.
  6. Middleware für die Authentifizierung (UseAuthentication) versucht, den Benutzer zu authentifizieren, bevor der Zugriff auf sichere Ressourcen zugelassen wird.
  7. Autorisierungsmiddleware (UseAuthorization) wird zum Autorisieren des Zugriffs auf sichere Ressourcen eines Benutzers verwendet.
  8. Middleware für Sitzungen (UseSession) richtet einen Sitzungsstatus ein und erhält diesen aufrecht. Wenn die App den Sitzungsstatus verwendet, rufen Sie die Middleware für Sitzungen nach der Middleware für Cookierichtlinien 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. Aus diesem Grund fängt die Middleware für den Ausnahmehandler 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 Middleware für statische Dateien stellt keine Autorisierungsüberprüfungen bereit. Alle Dateien, die von Middleware für statische Dateien unterstützt 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 Middleware für statische Dateien verarbeitet wird, wird sie an die Authentifizierungsmiddleware (UseAuthentication) übergeben, welche die Authentifizierung durchführt. Die Authentifizierung schließt nicht authentifizierte Anforderungen nicht kurz. Auch wenn die Authentifizierungsmiddleware Anforderungen authentifiziert, erfolgt die Autorisierung (und Ablehnung) erst dann, wenn MVC eine spezifische Razor Page oder einen MVC-Controller und eine Aktion ausgewählt hat.

Im folgenden Beispiel wird eine Middlewarereihenfolge veranschaulicht, bei der Anforderungen statischer Dateien von der Middleware für statische Dateien vor der Middleware für die Antwortkomprimierung verarbeitet werden. Die statischen Dateien werden bei dieser Middlewarereihenfolge 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();

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

Reihenfolge von UseCors und UseStaticFiles

Die Reihenfolge der Aufrufe von UseCors und UseStaticFiles hängt von der App ab. Weitere Informationen finden Sie unter Reihenfolge von UseCors und UseStaticFiles

Middleware für weitergeleitete Header: Auftrag

Middleware für weitergeleitete Header muss vor anderen Middlewarekomponenten ausgeführt werden. Mit dieser Reihenfolge wird sichergestellt, dass die auf Informationen von weitergeleiteten Headern basierende Middleware die zu verarbeitenden Headerwerte nutzen kann. Unter Middleware für weitergeleitete Header: Auftrag finden Sie Informationen zum Ausführen der Middleware für weitergeleitete Header nach der diagnostischen Middleware und der Middleware für die Fehlerbehandlung.

Branchen der Middlewarepipeline

Map-Erweiterungen werden als Konvention zum Branchen der Pipeline verwendet. Map brancht die Anforderungspipeline auf Grundlage von Übereinstimmungen des angegebenen Anforderungspfads. Wenn der Anforderungspfad mit dem angegebenen Pfad beginnt, wird der Branch ausgeführt.

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

app.Map("/map1", HandleMapTest1);

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

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

app.Run();

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

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

In der folgenden Tabelle sind die Anforderungen und Antworten von http://localhost:1234 mit dem oben stehenden Code aufgelistet.

Anforderung Antwort
localhost:1234 Hello from non-Map delegate.
localhost:1234/map1 Map Test 1
localhost:1234/map2 Map Test 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 das Schachteln, wie z.B. in folgendem Code:

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 zuordnen:

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

app.Map("/map1/seg1", HandleMultiSeg);

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

app.Run();

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

MapWhen brancht die Anforderungspipeline auf Grundlage des Ergebnisses 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 der Abfragezeichenfolgenvariablen branch zu ermitteln:

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 non-Map delegate.");
});

app.Run();

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 von http://localhost:1234 mit dem oben stehenden Code aufgelistet:

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

UseWhen brancht auch die Anforderungspipeline auf Grundlage des Ergebnisses 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:

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

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

Im vorherigen Beispiel wird die Antwort Hello from non-Map delegate. für alle Anforderungen ausgegeben. Wenn die Anforderung eine Abfragezeichenfolgevariable branch enthält, wird der Wert der Pipeline protokolliert, bevor eine neue Verbindung hergestellt wird.

Integrierte Middleware

Die folgenden Middlewarekomponenten sind im Lieferumfang von ASP.NET Core enthalten. Die Spalte Reihenfolge enthält Hinweise zur Platzierung der Middleware in der Pipeline, die die Anforderung verarbeitet, und zu den Bedingungen, unter denen die Middleware die Anforderungsverarbeitung möglicherweise beendet. Wenn eine Middleware einen Kurzschluss in der Anforderungsverarbeitungspipeline verursacht und verhindert, dass Downstreammiddleware eine Anforderung verarbeitet, wird diese als Terminalmiddleware bezeichnet. Weitere Informationen zu Kurzschlüssen finden Sie im Abschnitt Erstellen einer Middlewarepipeline mit WebApplication.

Middleware Beschreibung Auftrag
Authentifizierung Bietet Unterstützung für Authentifizierungen. Bevor HttpContext.User erforderlich ist. Terminal für OAuth-Rückrufe.
Autorisierung Bietet Unterstützung für Authentifizierungen Direkt nach der Authentifizierungsmiddleware
Cookierichtlinie Verfolgt die Zustimmung von Benutzern zum Speichern persönlicher Informationen und erzwingt die Mindeststandards für cookiefelder, z. B. secure und SameSite. Befindet sich vor der Middleware, die cookies ausstellt. Beispiele: Authentifizierung, Sitzung, MVC (TempData).
CORS Konfiguriert die Ressourcenfreigabe zwischen verschiedenen Ursprüngen (Cross-Origin Resource Sharing, CORS). Vor Komponenten, die CORS verwenden. UseCors muss sich derzeit aufgrund dieses Fehlers vor UseResponseCaching befinden.
DeveloperExceptionPage Diese generiert eine Seite mit Fehlerinformationen, die nur für die Verwendung in der Entwicklungsumgebung vorgesehen sind. Vor Komponenten, die Fehler erzeugen. Die Projektvorlagen registrieren diese Middleware automatisch als erste Middleware in der Pipeline, wenn die Umgebung Entwicklung ist.
Diagnose Mehrere separate Middlewares, die Entwicklern eine Ausnahmeseite, Ausnahmebehandlung, Statuscodeseiten und die Standardwebseite für neue Apps bereitstellen. Vor Komponenten, die Fehler erzeugen. Terminal für Ausnahmen oder zum Bereitstellen der Standardwebseite für neue Apps.
Weitergeleitete Header Leitet Proxyheader an die aktuelle Anforderung weiter. Vor Komponenten, die die aktualisierten Felder nutzen. 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. 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
HTTP-Protokollierung Hiermit werden HTTP-Anforderungen und -Antworten protokolliert. Am Anfang der Middlewarepipeline.
Außerkraftsetzung der HTTP-Methode Ermöglicht es eingehenden POST-Anforderungen, die Methode außer Kraft zu setzen. Vor Komponenten, die die aktualisierte Methode nutzen.
HTTPS-Umleitung Leitet alle HTTP-Anforderungen an HTTPS um. Vor Komponenten, die die URL nutzen.
HTTP Strict Transport Security (HSTS) Middleware für erweiterte Sicherheit, die einen besonderen Antwortheader hinzufügt. Bevor Antworten gesendet werden und nach Komponenten, die Anforderungen ändern. Beispiele: weitergeleitete Header, URL-Umschreibung.
MVC Verarbeitet Anforderungen mit MVC/Razor Pages. Abschließend, wenn eine Anforderung mit einer Route übereinstimmt.
OWIN Interoperabilität mit auf OWIN basierten Apps, Servern und Middleware. Abschließend, wenn die OWIN-Middleware die Anforderung vollständig verarbeitet.
Anforderungsdekomprimierung Bietet Unterstützung für das Dekomprimieren von Anforderungen. Vor Komponenten, die den Anforderungstext lesen.
Zwischenspeichern von Antworten Bietet Unterstützung für das Zwischenspeichern von Antworten. Vor Komponenten, für die das Zwischenspeichern erforderlich ist. UseCORS muss sich vor UseResponseCaching befinden.
Antwortkomprimierung Bietet Unterstützung für das Komprimieren von Antworten. Vor Komponenten, für die das Komprimieren erforderlich ist.
Lokalisierung von Anforderungen Bietet Unterstützung für die Lokalisierung. Vor der Lokalisierung vertraulicher Komponenten. Muss hinter Routing-Middleware stehen, wenn RouteDataRequestCultureProvider verwendet wird.
Endpunktrouting Definiert Anforderungsrouten und schränkt diese ein. Terminal für entsprechende Routen.
SPA Verarbeitet alle Anforderungen ab diesem Punkt in der Middlewarekette, indem die Standardseite für die Single-Page-Anwendung zurückgegeben wird. Kommt spät in der Kette, sodass andere Middleware, z. B. die zum Bereitstellen von statischen Dateien oder MVC-Aktionen, Vorrang hat.
Sitzung Bietet Unterstützung für das Verwalten von Benutzersitzungen. Vor Komponenten, für die Sitzungen erforderlich sind.
Statische Dateien Bietet Unterstützung für das Verarbeiten statischer Dateien und das Durchsuchen des Verzeichnisses. 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. Vor Komponenten, die die URL nutzen.
W3CLogging Hiermit werden Serverzugriffsprotokolle im erweiterten W3C-Protokolldateiformat generiert. Am Anfang der Middlewarepipeline.
WebSockets Aktiviert das WebSockets-Protokoll. Vor Komponenten, die WebSocket-Anforderungen annehmen müssen.

Zusätzliche Ressourcen

Von Rick Anderson und Steve Smith

Middleware ist Software, die zu einer Anwendungspipeline zusammengesetzt wird, um Anforderungen und Antworten zu verarbeiten. Jede Komponente kann Folgendes ausführen:

  • Entscheiden, ob die Anforderung an die nächste Komponente in der Pipeline übergeben werden soll.
  • Ausführen von Arbeiten, bevor oder nachdem die nächste Komponente in der Pipeline aufgerufen wird.

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

Anforderungsdelegaten werden mit den Erweiterungsmethoden Run, Map und Use konfiguriert. Ein einzelner Anforderungsdelegat kann inline als anonyme Methode angegeben werden (sogenannte Inline-Middleware), oder er kann in einer wiederverwendbaren Klasse definiert werden. Diese wiederverwendbaren Klassen und anonymen Inline-Methoden sind Middleware bzw. Middlewarekomponenten. Jede Middlewarekomponente in der Anforderungspipeline ist für das Aufrufen der jeweils nächsten Komponente in der Pipeline oder, wenn nötig, für das Kurzschließen der Pipeline zuständig. Wenn eine Middleware einen Kurzschluss verursacht, wird diese als Terminalmiddleware bezeichnet, da sie verhindert, dass weitere Middleware die Anforderung verarbeiten kann.

Unter Migrieren von HTTP-Handlern und -Modulen zu ASP.NET Core-Middleware wird der Unterschied zwischen Anforderungspipelines in ASP.NET Core und ASP.NET 4.x erläutert, und es werden zusätzliche Beispiele für Middleware bereitgestellt.

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.

Request processing pattern showing a request arriving, processing through three middlewares, and the response leaving the app. Each middleware runs its logic and hands off the request to the next middleware at the next() statement. After the third middleware processes the request, the request passes back through the prior two middlewares in reverse order for additional processing after their next() statements before leaving the app as a response to the client.

Jeder Delegat kann Vorgänge vor und nach dem nächsten Delegaten ausführen. Die Ausnahmebehandlungsdelegaten müssen am Anfang der Pipeline aufgerufen werden, sodass sie Ausnahmen abfangen können, die zu einem späteren Zeitpunkt in der Pipeline ausgelöst werden.

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 Anforderungedelegate 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 den Parameter nextnicht aufrufen. Normalerweise können Sie Aktionen sowohl vor als auch nach dem nächsten Delegaten durchführen. Dies wird in folgendem Beispiel veranschaulicht:

public class Startup
{
    public void Configure(IApplicationBuilder app)
    {
        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.
        });

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

Wenn ein keine Anforderung an den nächsten Delegaten übergibt, wird dies als Kurzschluss der Anforderungspipeline bezeichnet. Das Kurzschließen ist oft sinnvoll, da es unnötige Arbeit verhindert. Die Middleware für statische Dateien kann beispielsweise als Terminalmiddleware fungieren, indem sie eine Anforderung für eine statische Datei zurückgibt und den Rest der Pipeline kurzschließt. 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. An HttpResponse vorgenommene Änderungen 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, wenn Sie z.B. mehr als das genannte Content-Length-Objekt 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, der angibt, ob Header gesendet wurden oder ob in den Text geschrieben wurde.

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

public class Startup
{
    public void Configure(IApplicationBuilder app)
    {
        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.
        });

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

Wenn Sie möchten, dass Codekommentare in anderen Sprachen als Englisch angezeigt werden, informieren Sie uns in diesem GitHub-Issue.

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.

Middlewarereihenfolge

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 neu angeordnet oder neue benutzerdefinierte Middleware nach Bedarf eingefügt wird.

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).

ASP.NET Core filter pipeline

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

Für den Code oben gilt:

  • Middleware, die beim Erstellen einer neuen Web-App mit einzelnen Benutzerkonten nicht hinzugefügt wird, wird auskommentiert.
  • 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 aufgrund dieses Fehlers vor UseResponseCaching stehen.
    • 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. Zum 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-/Fehlerbehandlung
    • Bei Ausführung der App in der Entwicklungsumgebung:
      • Middleware der Seite mit Ausnahmen für Entwickler (UseDeveloperExceptionPage) meldet App-Laufzeitfehler.
      • Middleware der Seite mit Datenbankfehlern meldet Datenbanklaufzeitfehler.
    • Bei Ausführung der App in der Produktionsumgebung:
      • Middleware für Ausnahmehandler (UseExceptionHandler) fängt Ausnahmen ab, die in den folgenden Middlewarekomponenten ausgelöst werden.
      • HTTP Strict Transport Security Protocol-Middleware (HSTS) (UseHsts) fügt den Strict-Transport-Security-Header hinzu.
  2. Middleware zur HTTPS-Umleitung (UseHttpsRedirection) leitet HTTP-Anforderungen an HTTPS um.
  3. Middleware für statische Dateien (UseStaticFiles) gibt statische Dateien zurück und umgeht die weitere Anforderungsverarbeitung.
  4. Middleware für Cookierichtlinien (UseCookiePolicy) sorgt dafür, dass die App die Anforderungen der europäischen Datenschutzgrundverordnung (DSGVO) erfüllt.
  5. Routingmiddleware (UseRouting) zum Weiterleiten von Anforderungen.
  6. Middleware für die Authentifizierung (UseAuthentication) versucht, den Benutzer zu authentifizieren, bevor der Zugriff auf sichere Ressourcen zugelassen wird.
  7. Autorisierungsmiddleware (UseAuthorization) wird zum Autorisieren des Zugriffs auf sichere Ressourcen eines Benutzers verwendet.
  8. Middleware für Sitzungen (UseSession) richtet einen Sitzungsstatus ein und erhält diesen aufrecht. Wenn die App den Sitzungsstatus verwendet, rufen Sie die Middleware für Sitzungen nach der Middleware für Cookierichtlinien 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. Aus diesem Grund fängt die Middleware für den Ausnahmehandler 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 Middleware für statische Dateien stellt keine Autorisierungsüberprüfungen bereit. Alle Dateien, die von Middleware für statische Dateien unterstützt 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 Middleware für statische Dateien verarbeitet wird, wird sie an die Authentifizierungsmiddleware (UseAuthentication) übergeben, welche die Authentifizierung durchführt. Die Authentifizierung schließt nicht authentifizierte Anforderungen nicht kurz. Auch wenn die Authentifizierungsmiddleware Anforderungen authentifiziert, erfolgt die Autorisierung (und Ablehnung) erst dann, wenn MVC eine spezifische Razor Page oder einen MVC-Controller und eine Aktion ausgewählt hat.

Im folgenden Beispiel wird eine Middlewarereihenfolge veranschaulicht, bei der Anforderungen statischer Dateien von der Middleware für statische Dateien vor der Middleware für die Antwortkomprimierung verarbeitet werden. Die statischen Dateien werden bei dieser Middlewarereihenfolge 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 kommt zuletzt, damit:

  • Jede andere Middleware zuerst auf entsprechende Anforderungen reagieren können.
  • 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.

Middleware für weitergeleitete Header: Auftrag

Middleware für weitergeleitete Header muss vor anderen Middlewarekomponenten ausgeführt werden. Mit dieser Reihenfolge wird sichergestellt, dass die auf Informationen von weitergeleiteten Headern basierende Middleware die zu verarbeitenden Headerwerte nutzen kann. Unter Middleware für weitergeleitete Header: Auftrag finden Sie Informationen zum Ausführen der Middleware für weitergeleitete Header nach der diagnostischen Middleware und der Middleware für die Fehlerbehandlung.

Branchen der Middlewarepipeline

Map-Erweiterungen werden als Konvention zum Branchen der Pipeline verwendet. Map brancht die Anforderungspipeline auf Grundlage von Übereinstimmungen des angegebenen Anforderungspfads. Wenn der Anforderungspfad mit dem angegebenen Pfad beginnt, wird der Branch 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 Anforderungen und Antworten von http://localhost:1234 mit dem oben stehenden Code aufgelistet.

Anforderung Antwort
localhost:1234 Hello from non-Map delegate.
localhost:1234/map1 Map Test 1
localhost:1234/map2 Map Test 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 das Schachteln, wie z.B. in folgendem Code:

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 zuordnen:

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 brancht die Anforderungspipeline auf Grundlage des Ergebnisses 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 der Abfragezeichenfolgenvariablen branch zu ermitteln:

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 Anforderungen und Antworten von http://localhost:1234 mit dem oben stehenden Code aufgelistet:

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

UseWhen brancht auch die Anforderungspipeline auf Grundlage des Ergebnisses 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);

            // 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 der Wert der Pipeline protokolliert, bevor eine neue Verbindung hergestellt wird.

Integrierte Middleware

Die folgenden Middlewarekomponenten sind im Lieferumfang von ASP.NET Core enthalten. Die Spalte Reihenfolge enthält Hinweise zur Platzierung der Middleware in der Pipeline, die die Anforderung verarbeitet, und zu den Bedingungen, unter denen die Middleware die Anforderungsverarbeitung möglicherweise beendet. Wenn eine Middleware einen Kurzschluss in der Anforderungsverarbeitungspipeline verursacht und verhindert, dass Downstreammiddleware eine Anforderung verarbeitet, wird diese als Terminalmiddleware bezeichnet. Weitere Informationen zu Kurzschlüssen finden Sie im Abschnitt Erstellen einer Middlewarepipeline mit IApplicationBuilder.

Middleware Beschreibung Auftrag
Authentifizierung Bietet Unterstützung für Authentifizierungen. Bevor HttpContext.User erforderlich ist. Terminal für OAuth-Rückrufe.
Autorisierung Bietet Unterstützung für Authentifizierungen Direkt nach der Authentifizierungsmiddleware
Cookierichtlinie Verfolgt die Zustimmung von Benutzern zum Speichern persönlicher Informationen und erzwingt die Mindeststandards für cookiefelder, z. B. secure und SameSite. Befindet sich vor der Middleware, die cookies ausstellt. Beispiele: Authentifizierung, Sitzung, MVC (TempData).
CORS Konfiguriert die Ressourcenfreigabe zwischen verschiedenen Ursprüngen (Cross-Origin Resource Sharing, CORS). Vor Komponenten, die CORS verwenden. UseCors muss sich derzeit aufgrund dieses Fehlers vor UseResponseCaching befinden.
Diagnose Mehrere separate Middlewares, die Entwicklern eine Ausnahmeseite, Ausnahmebehandlung, Statuscodeseiten und die Standardwebseite für neue Apps bereitstellen. Vor Komponenten, die Fehler erzeugen. Terminal für Ausnahmen oder zum Bereitstellen der Standardwebseite für neue Apps.
Weitergeleitete Header Leitet Proxyheader an die aktuelle Anforderung weiter. Vor Komponenten, die die aktualisierten Felder nutzen. 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. 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
Außerkraftsetzung der HTTP-Methode Ermöglicht es eingehenden POST-Anforderungen, die Methode außer Kraft zu setzen. Vor Komponenten, die die aktualisierte Methode nutzen.
HTTPS-Umleitung Leitet alle HTTP-Anforderungen an HTTPS um. Vor Komponenten, die die URL nutzen.
HTTP Strict Transport Security (HSTS) Middleware für erweiterte Sicherheit, die einen besonderen Antwortheader hinzufügt. Bevor Antworten gesendet werden und nach Komponenten, die Anforderungen ändern. Beispiele: weitergeleitete Header, URL-Umschreibung.
MVC Verarbeitet Anforderungen mit MVC/Razor Pages. Abschließend, wenn eine Anforderung mit einer Route übereinstimmt.
OWIN Interoperabilität mit auf OWIN basierten Apps, Servern und Middleware. Abschließend, wenn die OWIN-Middleware die Anforderung vollständig verarbeitet.
Zwischenspeichern von Antworten Bietet Unterstützung für das Zwischenspeichern von Antworten. Vor Komponenten, für die das Zwischenspeichern erforderlich ist. UseCORS muss sich vor UseResponseCaching befinden.
Antwortkomprimierung Bietet Unterstützung für das Komprimieren von Antworten. Vor Komponenten, für die das Komprimieren erforderlich ist.
Lokalisierung von Anforderungen Bietet Unterstützung für die Lokalisierung. Vor der Lokalisierung vertraulicher Komponenten. Muss hinter Routing-Middleware stehen, wenn RouteDataRequestCultureProvider verwendet wird.
Endpunktrouting Definiert Anforderungsrouten und schränkt diese ein. Terminal für entsprechende Routen.
SPA Verarbeitet alle Anforderungen ab diesem Punkt in der Middlewarekette, indem die Standardseite für die Single-Page-Anwendung zurückgegeben wird. Kommt spät in der Kette, sodass andere Middleware, z. B. die zum Bereitstellen von statischen Dateien oder MVC-Aktionen, Vorrang hat.
Sitzung Bietet Unterstützung für das Verwalten von Benutzersitzungen. Vor Komponenten, für die Sitzungen erforderlich sind.
Statische Dateien Bietet Unterstützung für das Verarbeiten statischer Dateien und das Durchsuchen des Verzeichnisses. 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. Vor Komponenten, die die URL nutzen.
WebSockets Aktiviert das WebSocket-Protokoll. Vor Komponenten, die WebSocket-Anforderungen annehmen müssen.

Zusätzliche Ressourcen