Sisk

Anfrageverarbeitung

Diese Seite wurde automatisch aus dem Englischen übersetzt. Original lesen

Request-Handler, auch als „Middlewares“ bezeichnet, sind Funktionen, die vor oder nach der Ausführung einer Anfrage im Router laufen. Sie können pro Route oder pro Router definiert werden.

Es gibt zwei Arten von Request-Handlern:

  • BeforeResponse: definiert, dass der Request-Handler vor dem Aufruf der Router‑Aktion ausgeführt wird.
  • AfterResponse: definiert, dass der Request-Handler nach dem Aufruf der Router‑Aktion ausgeführt wird. Das Senden einer HTTP‑Antwort in diesem Kontext überschreibt die Antwort der Router‑Aktion.

Beide Request-Handler können die eigentliche Rückgabe der Router‑Callback‑Funktion überschreiben. Übrigens können Request-Handler nützlich sein, um eine Anfrage zu validieren, z. B. Authentifizierung, Inhalt oder andere Informationen, sowie zum Speichern von Daten, Protokollierung oder anderen Schritten, die vor oder nach einer Antwort ausgeführt werden können.

Auf diese Weise kann ein Request-Handler die gesamte Ausführung unterbrechen und eine Antwort zurückgeben, bevor der Zyklus abgeschlossen ist, wobei alles andere verworfen wird.

Beispiel: Angenommen, ein Request-Handler zur Benutzer‑Authentifizierung authentifiziert den Nutzer nicht. Er verhindert, dass der Anfragen‑Lebenszyklus fortgesetzt wird, und lässt die Verarbeitung hängen. Wenn dies im Request-Handler an zweiter Stelle geschieht, werden der dritte und alle nachfolgenden nicht mehr ausgewertet.

Erstellen eines Request‑Handlers #

Um einen Request‑Handler zu erstellen, können wir eine Klasse anlegen, die das IRequestHandler-Interface implementiert, in folgendem Format:

Middleware/AuthenticateUserRequestHandler.csC#
public class AuthenticateUserRequestHandler : IRequestHandler
{
    public RequestHandlerExecutionMode ExecutionMode { get; init; } = RequestHandlerExecutionMode.BeforeResponse;

    public HttpResponse? Execute(HttpRequest request, HttpContext context)
    {
        if (request.Headers.Authorization != null)
        {
            // Rückgabe von null bedeutet, dass der Anfragenzyklus fortgesetzt werden kann
            return null;
        }
        else
        {
            // Rückgabe eines HttpResponse-Objekts bedeutet, dass diese Antwort benachbarte Antworten überschreibt.
            return new HttpResponse(System.Net.HttpStatusCode.Unauthorized);
        }
    }
}

Im obigen Beispiel haben wir angegeben, dass, wenn der Authorization‑Header in der Anfrage vorhanden ist, die Verarbeitung fortgesetzt und der nächste Request‑Handler oder der Router‑Callback aufgerufen werden soll, je nachdem, was als Nächstes kommt. Wird ein Request‑Handler nach der Antwort ausgeführt (über die Eigenschaft ExecutionMode) und gibt einen von null verschiedenen Wert zurück, überschreibt er die Antwort des Routers.

Gibt ein Request‑Handler null zurück, bedeutet das, dass die Anfrage fortgesetzt werden muss und das nächste Objekt aufgerufen wird bzw. der Zyklus mit der Antwort des Routers endet.

Warnung

Eine Request‑Handler‑Instanz wird einmal erstellt und von allen Anfragen, die sie verarbeitet, gemeinsam genutzt, einschließlich gleichzeitiger Anfragen. Speichern Sie niemals zustandsbezogene Daten einer einzelnen Anfrage in der Request‑Handler‑Klasse selbst (Felder oder Eigenschaften wie der aktuelle Benutzer, ein geparstes Token oder ein Timer), da dieser Wert in andere Anfragen „leckt“. Speichern Sie anfragebezogene Werte stattdessen im RequestBag des HttpContext:

C#
public HttpResponse? Execute(HttpRequest request, HttpContext context)
{
    // falsch: this.currentUser = FindUser(request);
    context.RequestBag.Set(FindUser(request));
    return null;
}

Wenn Sie von der integrierten Klasse RequestHandler erben, können Sie Next() zurückgeben, um diese Absicht explizit zu machen:

C#
public class AuthenticateUserRequestHandler : RequestHandler
{
    public override HttpResponse? Execute(HttpRequest request, HttpContext context)
    {
        if (request.Headers.Authorization is not null)
            return Next();

        return new HttpResponse(System.Net.HttpStatusCode.Unauthorized);
    }
}

Für Handler, die I/O benötigen, erben Sie von AsyncRequestHandler:

C#
public class LoadUserRequestHandler : AsyncRequestHandler
{
    public override async Task<HttpResponse?> ExecuteAsync(HttpRequest request, HttpContext context)
    {
        var user = await UserRepository.FindAsync(request.Headers.Authorization, request.DisconnectToken);
        if (user is null)
            return new HttpResponse(System.Net.HttpStatusCode.Unauthorized);

        request.Bag.Set(user);
        return Next();
    }
}

Kleine Inline‑Handler können ebenfalls mit RequestHandler.Create oder AsyncRequestHandler.Create erstellt werden:

C#
var requireJson = RequestHandler.Create((request, context) =>
{
    if (request.Headers.ContentType?.Contains("application/json") == true)
        return null;

    return new HttpResponse(System.Net.HttpStatusCode.UnsupportedMediaType);
});

Zuordnen eines Request‑Handlers zu einer einzelnen Route #

Sie können einer Route einen oder mehrere Request‑Handler zuweisen.

Router.csC#
mainRouter.Map(RouteMethod.Get, "/", IndexPage, new IRequestHandler[]
{
    new AuthenticateUserRequestHandler(),     // Request‑Handler vor der Anfrage
    new ValidateJsonContentRequestHandler(),  // Request‑Handler vor der Anfrage
    //                                        -- Methode IndexPage wird hier ausgeführt
    new WriteToLogRequestHandler()            // Request‑Handler nach der Anfrage
});

Oder beim Erstellen eines Route-Objekts:

Router.csC#
Route indexRoute = Route.Get("/", IndexPage);
indexRoute.RequestHandlers = new IRequestHandler[]
{
    new AuthenticateUserRequestHandler()
};
mainRouter.Map(indexRoute);

Zuordnen eines Request‑Handlers zu einem Router #

Sie können einen globalen Request‑Handler definieren, der auf allen Routen eines Routers ausgeführt wird.

Router.csC#
mainRouter.GlobalRequestHandlers = new IRequestHandler[]
{
    new AuthenticateUserRequestHandler()
};

Zuordnen eines Request‑Handlers zu einem Attribut #

Sie können einen Request‑Handler als Method-Attribut zusammen mit einem Route‑Attribut definieren.

Controller/MyController.csC#
public class MyController
{
    [RouteGet("/")]
    [RequestHandler<AuthenticateUserRequestHandler>]
    static HttpResponse Index(HttpRequest request)
    {
        return new HttpResponse() {
            Content = new StringContent("Hello world!")
        };
    }
}

Beachten Sie, dass der gewünschte Request‑Handler‑Typ und nicht eine Objektinstanz übergeben werden muss. Auf diese Weise wird der Request‑Handler vom Router‑Parser instanziiert. Sie können Argumente im Klassenkonstruktor über die Eigenschaft ConstructorArguments übergeben.

Beispiel:

Controller/MyController.csC#
[RequestHandler<AuthenticateUserRequestHandler>("arg1", 123, ...)]
public HttpResponse Index(HttpRequest request)
{
    return res = new HttpResponse() {
        Content = new StringContent("Hello world!")
    };
}

Sie können auch Ihr eigenes Attribut erstellen, das RequestHandler implementiert:

Middleware/Attributes/AuthenticateAttribute.csC#
public class AuthenticateAttribute : RequestHandlerAttribute
{
    public AuthenticateAttribute() : base(typeof(AuthenticateUserRequestHandler), ConstructorArguments = new object?[] { "arg1", 123, ... })
    {
        ;
    }
}

Und verwenden Sie es so:

Controller/MyController.csC#
[Authenticate]
static HttpResponse Index(HttpRequest request)
{
    return res = new HttpResponse() {
        Content = new StringContent("Hello world!")
    };
}

Umgehen eines globalen Request‑Handlers #

Nachdem Sie einen globalen Request‑Handler für eine Route definiert haben, können Sie diesen Request‑Handler für bestimmte Routen ignorieren.

Router.csC#
var myRequestHandler = new AuthenticateUserRequestHandler();
mainRouter.GlobalRequestHandlers = new IRequestHandler[]
{
    myRequestHandler
};

Route publicRoute = Route.Get("/", IndexPage);
publicRoute.Name = "My route";
publicRoute.BypassGlobalRequestHandlers = new IRequestHandler[]
{
    myRequestHandler,                    // ok: dieselbe Instanz wie in den globalen Request‑Handlern
    new AuthenticateUserRequestHandler() // falsch: wird den globalen Request‑Handler nicht überspringen
};

mainRouter.Map(publicRoute);

Hinweis

Wenn Sie einen Request‑Handler umgehen, müssen Sie dieselbe Referenz verwenden, die Sie zuvor instanziiert haben, um ihn zu überspringen. Das Erstellen einer anderen Request‑Handler‑Instanz wird den globalen Request‑Handler nicht überspringen, da sich die Referenz ändert. Denken Sie daran, dieselbe Request‑Handler‑Referenz zu verwenden, die sowohl in GlobalRequestHandlers als auch in BypassGlobalRequestHandlers angegeben ist.

Tippen, um die Dokumentation und die API-Referenz zu durchsuchen.