In diesem Blog berichte ich über Middleware in ASP.NET Core. Ich erkläre am Anfang kurz, was ein Middleware in ASP.NET Core ist und was es macht. Danach zeige ich mit ein Beispiel, wie man ein eigenes Middleware erstellen kann.
Was ist ein Middleware?
Die ASP.NET Core Architektur verfügt über ein System von Middleware, die Requests und Responses verarbeiten. Ein Middleware ist nichts weiteres als ein Komponent (Klasse), was bei jedem Request in eine ASP.NET Core Applikation aufgerufen wird. In ASP.NET, HttpHandlers und HttpModules waren Teil der Request Pipeline. Die Middleware ist ähnlich wie HttpHandlers und HttpModules, wo beide in jedem Request konfiguriert und ausgeführt werden muss.
In ein ASP.NET Core Webapplikation gibt es normalerweise mehrere Middlewares. Sie sind entweder Middlewares vom Framework, Middlewares von NuGet Pakete oder selbst gecodete/benutzerdefinierte Middlewares. Die Reihenfolge für die Ausführung der Middlewares kann man im Request Pipeline feststellen. Nach der Reihenfolge sind die Middlewares nun auch verkettet, um eine Pipeline zu bilden. Eingehende Requests werden durch die Request Pipeline geleitet, wobei jede Middleware die Möglichkeit hat, etwas mit den Requests zu machen, bevor sie die Requests an die nächste Middleware weitergibt. Ausgehende Responses werden ebenfalls durch die Pipeline geleitet, allerdings in umgekehrter Reihenfolge. Die Microsoft Dokumentation zu ASP.NET Core Middleware hat ein Schema, die es gut darstellt:
Middlewares werden für sehr vieles benutzt. Ein paar Beispiele für bereits existierende Middlewares wären ExceptionHandling, MVC, Routing, Authentication und Authorization. In diesem Diagramm sieht man, wie diese Middlewares typischerweise geordnet sind und wo die “custom middlewares” hinzugefügt werden. Man hat die volle Kontrolle über die Reihenfolge.
Benutzerdefinierte Middleware
Es gibt zwei Varianten, wie man benutzerdefinierte Middlewares implementiert. Eine Variante wäre, eine inline Middleware zu implementieren und die andere wäre, eine Klasse zu erstellen.
Inline Middleware
Eine inline Middleware zu implementieren geht ganz einfach und man muss nur zwei Methoden kennen und unterscheiden können:
Run: Führt eigene Middleware aus und gibt die Kontrolle nicht zum nächsten Middleware im Request Pipeline weiter.
Use: Führt eigene Middleware aus, gibt aber die Kontrolle weiter zum nächsten Middleware im Request Pipeline, indem man die next() Methode aufruft.
Hier füge ich eine inline Middleware im Request Pipeline hinzu:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILogger<Startup> logger)
{
// Fügt middleware hinzu als erste Komponent, was in der Pipeline ausgeführt wird
app.Use(async (context, next) =>
{
logger.LogInformation("Inline Middleware executing...");
await next();
}
);
Da ich hier die Use() Methode benutzt habe, konnte ich mit await next() die nächste Middleware im Request Pipeline aufrufen.
Wenn ich nun eine Request mache, wird folgendes geloggt/protokolliert:
Middleware Klasse
Eine benutzerdefinierte Middleware Klasse ist wie jede andere Klasse mit der Invoke() Methode. Damit es aber die nächste Middleware in der Verkettung ausführt, braucht es noch ein Typ RequestDelegate Parameter im Konstruktor.
Visual Studio bietet ein Template an, um eine Middleware Klasse zu erstellen. Man klickt einfach auf “Add” → “New Item“.
Mit der Suchfunktion sucht man nach “Middleware” und die Liste wird auf die “Middleware Class” gefiltert:
Es wurde nun eine neue Klasse für die Middleware erstellt mit zusätzliche extension method:
// You may need to install the Microsoft.AspNetCore.Http.Abstractions package into your project
public class CustomMiddleware
{
private readonly RequestDelegate _next;
public CustomMiddleware(RequestDelegate next)
{
_next = next;
}
public Task Invoke(HttpContext httpContext)
{
return _next(httpContext);
}
}
// Extension method used to add the middleware to the HTTP request pipeline.
public static class CustomMiddlewareExtensions
{
public static IApplicationBuilder UseCustomMiddleware(this IApplicationBuilder builder)
{
return builder.UseMiddleware<CustomMiddleware>();
}
}
Das RequestDelegate _next wird benutzt, um die Kontrolle, nach der Ausführung der Middleware, dem nächsten Middleware zu geben. In der Invoke() Methode wird die Logik geschrieben. Zusätzliche Parameter werden durch Dependency Injection mitgegeben. Damit es aber Asynchron die nächste Middleware aufruft, muss noch das Schlüsselwort async und await hinzugefügt werden. Die extension method wird mit generiert und ist dafür da, die Middleware im Request Pipeline hinzuzufügen.
// You may need to install the Microsoft.AspNetCore.Http.Abstractions package into your project
public class CustomMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger _logger;
public CustomMiddleware(RequestDelegate next, ILoggerFactory loggerFactory)
{
_next = next;
_logger = loggerFactory.CreateLogger<CustomMiddleware>();
}
public async Task Invoke(HttpContext httpContext)
{
_logger.LogInformation("Test");
// Ruft nächste Middleware auf
await _next(httpContext);
}
}
// Extension method used to add the middleware to the HTTP request pipeline.
public static class CustomMiddlewareExtensions
{
public static IApplicationBuilder UseCustomMiddleware(this IApplicationBuilder builder)
{
return builder.UseMiddleware<CustomMiddleware>();
}
}
Hier wird die extension method benutzt:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// Fügt middleware hinzu als erste Komponent, was in der Pipeline ausgeführt wird
app.UseCustomMiddleware();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
Folgendes wird geloggt/protokolliert, wenn man ein Request macht: