Request pipeline w ASP.NET 5 działa na zasadzie middleware, co to znaczy?

Dla mnie najprościej było to zrozumieć poprzez analogię do głuchego telefonu, zabawy w którą chyba każdy z nas się bawił :)

Gluchy telefon

Gluchy telefon

Nie jestem pewny jakie są prawa autorskie tego obrazka, znalezione w sieci bez żadnego info

Chodzi o to, że jak wchodzi request to przekazywany jest on do pierwszej osoby, jeżeli jej nie ma, to request po prostu się kończy i tyle, nic się nie dzieje. Jeżeli osoba jest to teraz wszystko zależy od tej osoby: czy ona go zmodyfikuje? Przekaże dalej? Może nie chce się już bawić? Na tej samej zasadzie działa middleware.

Wchodzi request i przekazywany jest on do PIERWSZEGO zadeklarowanego middleware, ten zaś może z nim zrobić wszystko co chce i albo przekazać dalej, albo ustawić kontynuację jak wiadomość do niego wróci. Jeżeli middleware się zdecyduje by przekazać wiadomość dalej, trafi ona do DRUGIEGO zadeklarowanego middleware – nie do trzeciego czy czwartego, ale do DRUGIEGO. Jest to łańcuszek.

Czyli jeżeli nasz PIERWSZY middleware wywali błąd, zaś DRUGI ma go przechwycić to raczej nic z tego ;) gdyż do DRUGIEGO request nawet nie dojdzie, zostanie przerwany przy PIERWSZYM. To tak jak w głuchym telefonie, jak pierwsza osoba powie NIE BAWIE SIĘ, to druga nie usłyszy co ma przekazać dalej…

A więc bardzo ważna rzecz – KOLEJNOŚĆ w middleware ma znaczenie.

Ok a jak wygląda takie middleware? W najprostszej postaci tak:

app.Run(async ctx =>
{
    ctx.Response.StatusCode = 200;
    ctx.Response.ContentType = "text/html";
    await ctx.Response.WriteAsync("<h1>Hello, World!</h1>");
});

Jest to middleware które kończy request, nie zawiera ono delegata do wykonania kolejnego middleware, czyli jeżeli tutaj nic nie obsłużymy to już nic nie obsłużymy.

Inną opcją jest użycie middleware w takiej postaci:

app.Use(async (ctx, next) =>
    ctx.Response.ContentType = "text/html";
    await ctx.Response.WriteAsync("<h1>HACKED</h1>");
    await next();
});

Tym razem mamy delegat next który jak zostanie wykonany przekaże request do NASTĘPNEGO middleware. Jeżeli nie, to request się kończy i dalej nie pójdzie.

Istnieje jeszcze opcja stworzenia klasy implementującej middleware i następnie wykorzystanie jej za pomocą metody UseMiddleware:

public class HackMiddleware
{
    RequestDelegate _next;

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

    public async Task Invoke(HttpContext ctx)
    {
        await ctx.Response.WriteAsync("<h1>HACKED AGAIN!</h1>");
        await _next(ctx);
    }
}

// in Startup:

app.UseMiddleware<HackMiddleware>();

Większość dodatków do ASP.NET 5 korzysta do tego z extension methods by umożliwić prostsze rejestrowanie middleware (tak jak na przykład UseMvc):

public static class MyHackMiddlewareExtensions
{
    public static IApplicationBuilder UseHack(this IApplicationBuilder app)
    {
        return app.UseMiddleware<HackMiddleware>();
    }
}

// in Startup.cs:

app.UseHack();

Czy teraz jest to trochę bardziej zrozumiałe? Macie pomysły jak możecie to wykorzystać? :) Ja na przykład napisałem middleware który jak tylko gdzieś zobaczy napis „gutek” to zwraca mi stronę: „U Ciebie i tak nic nie zadziała Gutek” :)

4 KOMENTARZE

  1. Skończy się walką z przyjaznymi linkami w aplikacji, które chcemy tworzyć dynamicznie przy współpracy z bazą danych. Dróg do realizacji tego jest sporo, ale teraz widzę że wystarczy stworzyć Middlewere który połączy się z bazą i sprawdzi czy dla przekazanego URI nie można by dodać jakiś dodatkowych “ekstra” parametrów Request, które później złapie RouteMap :)

  2. Jakub Gutkowski o dev i nie tylko | ASP.NET 5 – middleware – krótko ale trafnie – Jakub Gutkowski o dev i nie tylko

    Dziękujemy za dodanie artykułu – Trackback z dotnetomaniak.pl

Comments are closed.