# Anfragen

Source: https://docs.sisk-framework.org/de/docs/fundamentals/requests.html

Anfragen sind Strukturen, die eine HTTP-Anforderungsnachricht darstellen. Das [HttpRequest](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpRequest.md) Objekt enthält nützliche Funktionen zur Verarbeitung von HTTP-Nachrichten in Ihrer Anwendung.

Eine HTTP-Anfrage besteht aus der Methode, dem Pfad, der Version, den Headern und dem Body.

In diesem Dokument zeigen wir Ihnen, wie Sie jedes dieser Elemente erhalten.

## Abrufen der Anfragemethode

Um die Methode der empfangenen Anfrage zu erhalten, können Sie die Property **Method** verwenden:

```cs
static HttpResponse Index(HttpRequest request)
{
    HttpMethod requestMethod = request.Method;
    ...
}
```

Diese Eigenschaft gibt die Methode der Anfrage zurück, dargestellt durch ein [HttpMethod](https://learn.microsoft.com/pt-br/dotnet/api/system.net.http.httpmethod) Objekt.

> [!NOTE]
> Im Gegensatz zu Routemethoden dient diese Eigenschaft nicht dem [RouteMethod.Any](https://docs.sisk-framework.org/api/Sisk.Core.Routing.RouteMethod.md) Element. Stattdessen gibt sie die tatsächliche Anfragemethode zurück.

## Abrufen von URL-Komponenten der Anfrage

Sie können verschiedene Komponenten einer URL über bestimmte Eigenschaften einer Anfrage erhalten. Für dieses Beispiel betrachten wir die URL:

```
http://localhost:5000/user/login?email=foo@bar.com
```

| Komponentenname | Beschreibung | Komponentenwert |
| --- | --- | --- |
| [Path](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpRequest.Path.md) | Gibt den Anforderungspfad zurück. | `/user/login` |
| [FullPath](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpRequest.FullPath.md) | Gibt den Anforderungspfad und die Abfragezeichenfolge zurück. | `/user/login?email=foo@bar.com` |
| [FullUrl](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpRequest.FullUrl.md) | Gibt die gesamte URL-Anforderungszeichenfolge zurück. | `http://localhost:5000/user/login?email=foo@bar.com` |
| [Host](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpRequest.Host.md) | Gibt den Host der Anfrage zurück. | `localhost` |
| [Authority](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpRequest.Authority.md) | Gibt den Host und Port der Anfrage zurück. | `localhost:5000` |
| [QueryString](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpRequest.QueryString.md) | Gibt die Abfrage der Anfrage zurück. | `?email=foo@bar.com` |
| [Query](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpRequest.Query.md) | Gibt die Abfrage der Anfrage in einer benannten Wertsammlung zurück. | `{StringValueCollection object}` |
| [IsSecure](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpRequest.IsSecure.md) | Bestimmt, ob die Anfrage SSL verwendet (true) oder nicht (false). | `false` |

Sie können auch die Property [HttpRequest.Uri](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpRequest.Uri.md) verwenden, die alles oben Genannte in einem Objekt enthält.

## Anforderungs‑Metadaten und Abbruch

Sisk fügt jeder Anfrage außerdem betriebliche Metadaten hinzu. Diese Eigenschaften sind nützlich für Protokolle, Tracing, Lokalisierung, Diagnose und langlaufende Vorgänge:

| Eigenschaft oder Methode | Verwendung |
| --- | --- |
| [RequestId](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpRequest.RequestId.md) | Ein eindeutiger Bezeichner für die Anfrage. Aktivieren Sie [IncludeRequestIdHeader](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpServerConfiguration.IncludeRequestIdHeader.md), um ihn als `X-Request-Id` zurückzugeben. |
| [RequestedAt](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpRequest.RequestedAt.md) | Der Zeitpunkt, zu dem Sisk das Anforderungsobjekt erstellt hat. |
| [RemoteAddress](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpRequest.RemoteAddress.md) | Die vom Verbindungsaufbau ermittelte Client-Adresse oder aus Ihrem [ForwardingResolver](https://docs.sisk-framework.org/de/docs/advanced/forwarding-resolvers.md). |
| [Culture](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpRequest.Culture.md) | Die aus `Accept-Language` ermittelte beste Kultur, mit Rückfall zur aktuellen Kultur. |
| [DisconnectToken](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpRequest.DisconnectToken.md) | Ein Abbruch‑Token, das ausgelöst wird, wenn der Client die Verbindung trennt, sofern die konfigurierte HTTP‑Engine dies unterstützt. |
| [Bag](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpRequest.Bag.md) | Ein typisierter Schlüssel/Wert‑Speicher, der über Anforderungs‑Handler und die Routinenaktion hinweg geteilt wird. |
| [GetRawHttpRequest](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpRequest.GetRawHttpRequest.md) | Eine Textdarstellung der Anfrage für Diagnosezwecke. |

## Abrufen des Anfragetextes

Einige Anfragen enthalten einen Body, z. B. Formulare, Dateien oder API‑Transaktionen. Sie können den Body einer Anfrage über die Property erhalten:

```cs
// ruft den Anfragetext als Zeichenkette ab, wobei die Anforderungs‑Codierung als Encoder verwendet wird
string body = request.Body;

// oder holt ihn als Byte‑Array
byte[] bodyBytes = request.RawBody;

// alternativ kann er gestreamt werden.
Stream requestStream = request.GetRequestStream();

// oder lese den Body asynchron
Memory<byte> bodyMemory = await request.GetBodyContentsAsync();
```

Es ist außerdem möglich, mit den Properties [HasContents](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpRequest.HasContents.md) und [IsContentAvailable](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpRequest.IsContentAvailable.md) zu bestimmen, ob ein Body vorhanden ist und ob er geladen wurde.

Es ist nicht möglich, den Anforderungsinhalt über `GetRequestStream` mehr als einmal zu lesen. Wenn Sie mit dieser Methode lesen, stehen die Werte in `RawBody` und `Body` ebenfalls nicht mehr zur Verfügung. Es ist nicht nötig, den Request‑Stream im Kontext der Anfrage zu entsorgen, da er am Ende der HTTP‑Sitzung, in der er erstellt wurde, freigegeben wird. Außerdem können Sie die Property [HttpRequest.RequestEncoding](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpRequest.RequestEncoding.md) verwenden, um die beste Codierung zum manuellen Dekodieren der Anfrage zu erhalten.

Der Server hat Beschränkungen für das Lesen des Anforderungsinhalts, die sowohl für [HttpRequest.Body](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpRequest.Body.md) als auch für [HttpRequest.RawBody](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpRequest.Body.md) gelten. Diese Eigenschaften kopieren den gesamten Eingabestream in einen lokalen Puffer der Größe von [HttpRequest.ContentLength](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpRequest.ContentLength.md).

Der Anforderungsinhalt ist durch [HttpServerConfiguration.MaximumContentLength](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpServerConfiguration.MaximumContentLength.md) begrenzt, das seit Sisk 1.7 standardmäßig 100 MB beträgt (zuvor war es `0`, unbegrenzt). Das Limit gilt wie folgt:

- Eine Anfrage, die einen `Content-Length` größer als das Limit angibt, erhält eine `413 Content Too Large`‑Antwort, bevor die Route aufgerufen wird.
- Ein chunked‑ oder gestreamter Body ohne deklarierte Länge wird während des Lesens geprüft. Das Überschreiten des Limits über [RawBody](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpRequest.RawBody.md), [Body](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpRequest.Body.md) oder den von [GetRequestStream](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpRequest.GetRequestStream.md) zurückgegebenen Stream wirft eine [InsufficientMemoryException](https://learn.microsoft.com/en-us/dotnet/api/system.insufficientmemoryexception), was zu einem `500 Internal Server Error` führt, sofern Ihr Code oder [Router.CallbackErrorHandler](https://docs.sisk-framework.org/api/Sisk.Core.Routing.Router.CallbackErrorHandler.md) dies nicht abfängt.
- Das gleiche Limit ist die maximale Größe einer empfangenen [WebSocket](https://docs.sisk-framework.org/de/docs/features/websockets.md) Nachricht.

Setzen Sie die Property auf `0`, um das Limit zu entfernen:

```cs
using var app = HttpServer.CreateBuilder()
    .UseConfiguration(config => config.MaximumContentLength = 0)
    .Build();
```

Selbst ohne konfiguriertes Limit können `Body` und `RawBody` keinen Inhalt größer als [Array.MaxLength](https://learn.microsoft.com/en-us/dotnet/api/system.array.maxlength) (etwa 2 GB) puffern und werfen eine Ausnahme, wenn Sie versuchen, darauf zuzugreifen. Größere Inhalte können weiterhin über Streaming verarbeitet werden.

> [!NOTE]
> Obwohl Sisk es erlaubt, ist es stets ratsam, den HTTP‑Semantiken zu folgen, um Ihre Anwendung zu erstellen und keinen Inhalt in Methoden zu erhalten oder zu liefern, die dies nicht zulassen. Lesen Sie über [RFC 9110 "HTTP Semantics"](https://httpwg.org/spec/rfc9110.html).

## Lesen von JSON-Anfragen

Für JSON‑APIs sollten Sie die integrierten JSON‑Hilfsfunktionen verwenden, anstatt `Body` zu lesen und manuell zu deserialisieren. Sie nutzen [System.Text.Json](https://learn.microsoft.com/en-us/dotnet/api/system.text.json) und greifen standardmäßig auf [HttpRequest.DefaultJsonSerializerOptions](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpRequest.DefaultJsonSerializerOptions.md) zurück.

```cs
public record CreateUserRequest(string Name, string Email);

router.MapPost("/users", (HttpRequest request) =>
{
    CreateUserRequest? body = request.GetJsonContent<CreateUserRequest>();
    if (body is null)
        return new HttpResponse(System.Net.HttpStatusCode.BadRequest);

    return new HttpResponse(System.Net.HttpStatusCode.Created);
});
```

Verwenden Sie die asynchrone Überladung, wenn Sie bereits in einer asynchronen Route sind oder die Anfragenabbruch‑Unterstützung die Deserialisierung stoppen soll:

```cs
router.MapPost("/users", async (HttpRequest request) =>
{
    CreateUserRequest? body =
        await request.GetJsonContentAsync<CreateUserRequest>(request.DisconnectToken);

    if (body is null)
        return new HttpResponse(System.Net.HttpStatusCode.BadRequest);

    return new HttpResponse(System.Net.HttpStatusCode.Created);
});
```

Sie können für einen bestimmten Endpunkt benutzerdefinierte [JsonSerializerOptions](https://learn.microsoft.com/en-us/dotnet/api/system.text.json.jsonserializeroptions) übergeben:

```cs
var options = new JsonSerializerOptions(JsonSerializerDefaults.Web)
{
    PropertyNameCaseInsensitive = true
};

UserDto? user = request.GetJsonContent<UserDto>(options);
```

Für Native‑AOT‑ oder trim‑sensible Anwendungen verwenden Sie die `JsonTypeInfo<T>`‑Überladung, die von einem `JsonSerializerContext` erzeugt wird:

```cs
[JsonSerializable(typeof(CreateUserRequest))]
public partial class AppJsonSerializerContext : JsonSerializerContext
{
}

CreateUserRequest? body =
    await request.GetJsonContentAsync(
        AppJsonSerializerContext.Default.CreateUserRequest,
        request.DisconnectToken);
```

Die gleiche „einmal‑lesen“-Regel gilt für JSON‑Hilfen: Nachdem Sisk den Request‑Stream über `GetJsonContent`, `GetJsonContentAsync`, `Body` oder `RawBody` gelesen hat, können Sie den gleichen Body später nicht mehr über `GetRequestStream()` konsumieren.

## Abrufen des Anforderungskontexts

Der HTTP‑Context ist ein exklusives Sisk‑Objekt, das Informationen über HTTP‑Server, Route, Router und Request‑Handler speichert. Sie können ihn nutzen, um sich in einer Umgebung zu organisieren, in der diese Objekte schwer zu handhaben sind.

Sie können den aktuell ausgeführten [HttpContext](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpContext.md) über die statische Methode `HttpContext.GetCurrentContext()` erhalten. Diese Methode gibt den Kontext der gerade in dem aktuellen Thread verarbeiteten Anfrage zurück.

```cs
HttpContext context = HttpContext.GetCurrentContext();
```

### Protokollmodus

Die Property [HttpContext.LogMode](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpContext.LogMode.md) ermöglicht es Ihnen, das Logging‑Verhalten für die aktuelle Anfrage zu steuern. Sie können das Logging für bestimmte Anfragen aktivieren oder deaktivieren und damit die Standard‑Serverkonfiguration überschreiben.

```cs
// Logging für diese Anfrage deaktivieren
context.LogMode = LogOutputMode.None;
```

### Request‑Bag

Das [RequestBag](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpContext.RequestBag.md) Objekt enthält gespeicherte Informationen, die von einem Request‑Handler an einen anderen Punkt weitergegeben werden und am Zielort konsumiert werden können. Dieses Objekt kann auch von Request‑Handlern verwendet werden, die nach dem Routencallback ausgeführt werden.

> [!TIP]
> Diese Eigenschaft ist auch über die [HttpRequest.Bag](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpRequest.Bag.md) Eigenschaft zugänglich.

```cs {title="Middleware/AuthenticateUserRequestHandler.cs"}
public class AuthenticateUserRequestHandler : IRequestHandler
{
    public string Identifier { get; init; } = Guid.NewGuid().ToString();
    public RequestHandlerExecutionMode ExecutionMode { get; init; } = RequestHandlerExecutionMode.BeforeResponse;
    
    public HttpResponse? Execute(HttpRequest request, HttpContext context)
    {
        if (request.Headers.Authorization != null)
        {
            context.RequestBag.Add("AuthenticatedUser", new User("Bob"));
            return null;
        }
        else
        {
            return new HttpResponse(System.Net.HttpStatusCode.Unauthorized);
        }
    }
}
```

Der obige Request‑Handler legt `AuthenticatedUser` im Request‑Bag ab und kann später im finalen Callback konsumiert werden:

```cs {title="Controller/MyController.cs"}
public class MyController
{
    [RouteGet("/")]
    [RequestHandler<AuthenticateUserRequestHandler>]
    static HttpResponse Index(HttpRequest request)
    {
        User authUser = request.Context.RequestBag["AuthenticatedUser"];
        
        return new HttpResponse() {
            Content = new StringContent($"Hello, {authUser.Name}!")
        };
    }
}
```

Sie können außerdem die Hilfsmethoden `Bag.Set()` und `Bag.Get()` verwenden, um Objekte anhand ihres Typs zu setzen bzw. zu holen.

Die Klasse `TypedValueDictionary` bietet ebenfalls die Methoden `GetValue` und `SetValue` für mehr Kontrolle.

```cs {title="Middleware/Authenticate.cs"}
public class Authenticate : RequestHandler
{
    public override HttpResponse? Execute(HttpRequest request, HttpContext context)
    {
        request.Bag.Set<User>(authUser);
    }
}
```

```csharp {title="Controller/MyController.cs"}
[RouteGet("/")]
[RequestHandler<Authenticate>]
public static HttpResponse GetUser(HttpRequest request)
{
    var user = request.Bag.Get<User>();
    ...
}
```

### Atomare Kontextwerte

Der Request‑Bag ist nicht synchronisiert. Wenn numerische Werte im Bag während derselben Anfrage von konkurrierendem Code geändert werden können, etwa durch parallele Tasks, die von der Route gestartet werden, verwenden Sie [HttpContext.Interlocked](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpContext.Interlocked.md). Sie gibt ein [HttpContextInterlocked](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpContext.HttpContextInterlocked.md) Objekt mit atomaren Operationen über benannte Werte des Request‑Bags zurück:

| Methode | Beschreibung |
| --- | --- |
| `Add(name, value)` | Addiert `value` zum gespeicherten Wert oder speichert `value`, wenn `name` nicht gesetzt ist. |
| `Exchange(name, value)` | Ersetzt den gespeicherten Wert und gibt den vorherigen zurück, oder `value`, wenn `name` nicht gesetzt war. |
| `CompareExchange(name, value, comparand, notFound = -1)` | Ersetzt den gespeicherten Wert durch `value` nur, wenn er dem `comparand` entspricht, und gibt den ursprünglichen Wert zurück, oder `notFound`, wenn `name` nicht gesetzt ist. |
| `Inspect(name)` | Gibt den gespeicherten Wert zurück, oder `null`, wenn `name` nicht gesetzt ist. |

Werte werden als `double` im Request‑Bag gespeichert. Überladungen, die `int` oder `long` erhalten, konvertieren sie zu `double`.

```cs
router.MapPost("/import", async (HttpRequest request) =>
{
    string[] lines = request.Body.Split('\n');

    await Parallel.ForEachAsync(lines, async (line, cancellation) =>
    {
        if (await TryImportAsync(line, cancellation))
            request.Context.Interlocked.Add("imported", 1);
    });

    double imported = request.Context.Interlocked.Inspect("imported") ?? 0;
    return new HttpResponse($"{imported} line(s) imported");
});
```

> [!NOTE]
> Die Operationen sind nur gegenüber anderen `Interlocked`‑Aufrufen atomar. Das Lesen oder Schreiben desselben Schlüssels direkt über `RequestBag` ist nicht mit ihnen synchronisiert.

## Abrufen von Formulardaten

Sie können Formulardatenwerte in einer [StringKeyStoreCollection](https://docs.sisk-framework.org/api/Sisk.Core.Entity.StringKeyStoreCollection.md) wie im folgenden Beispiel erhalten:

```cs {title="Controller/Auth.cs"}
[RoutePost("/auth")]
public HttpResponse Index(HttpRequest request)
{
    var form = request.GetFormContent();

    string? username = form["username"];
    string? password = form["password"];

    if (AttempLogin(username, password))
    {
        ...
    }
}
```

Die asynchrone Version ist nützlich, wenn der Request‑Body groß sein kann oder Sie eine Abbruch‑Unterstützung benötigen:

```cs
var form = await request.GetFormContentAsync(request.DisconnectToken);
```

## Abrufen von Multipart-Formulardaten

Sisk‑HTTP‑Requests ermöglichen das Abrufen hochgeladener Multipart‑Inhalte, wie Dateien, Formularfelder oder beliebiger Binärdaten.

```cs {title="Controller/Auth.cs"}
[RoutePost("/upload-contents")]
public HttpResponse Index(HttpRequest request)
{
    // Die folgende Methode liest die gesamte Anforderungs‑Eingabe in ein
    // Array von MultipartObjects ein.
    var multipartFormDataObjects = request.GetMultipartFormContent();
    
    foreach (MultipartObject uploadedObject in multipartFormDataObjects)
    {
        // Der Name der Datei, die durch Multipart‑Formulardaten bereitgestellt wird.
        // Null wird zurückgegeben, wenn das Objekt keine Datei ist.
        Console.WriteLine("File name       : " + uploadedObject.Filename);

        // Der Feldname des Multipart‑Formulardatenobjekts.
        Console.WriteLine("Field name      : " + uploadedObject.Name);

        // Die Inhaltslänge des Multipart‑Formulardatenobjekts.
        Console.WriteLine("Content length  : " + uploadedObject.ContentLength);

        // Bestimmt das Bildformat basierend auf dem Dateikopf für jeden
        // bekannten Inhaltstyp. Wenn der Inhalt kein erkanntes gängiges Dateiformat ist,
        // gibt diese Methode unten `MultipartObjectCommonFormat.Unknown` zurück.
        Console.WriteLine("Common format   : " + uploadedObject.GetCommonFileFormat());
    }
}
```

Verwenden Sie [GetMultipartFormContentAsync](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpRequest.GetMultipartFormContentAsync.md), wenn die Route asynchron ist:

```cs
var multipartFormDataObjects =
    await request.GetMultipartFormContentAsync(request.DisconnectToken);
```

Weitere Informationen zu Sisk‑[Multipart‑Form‑Objekten](https://docs.sisk-framework.org/api/Sisk.Core.Entity.MultipartObject.md) sowie zu deren Methoden, Eigenschaften und Funktionalitäten finden Sie in der Dokumentation.

## Erkennen von Client-Disconnects

Seit Version v1.15 von Sisk stellt das Framework über [HttpRequest.DisconnectToken](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpRequest.DisconnectToken.md) einen Abbruch‑Token bereit. Wenn die konfigurierte HTTP‑Engine die Erkennung von Disconnects unterstützt, wird dieser Token abgebrochen, sobald die Client‑Verbindung geschlossen wird, bevor die Antwort abgeschlossen ist. Das ist nützlich, um langlaufende Vorgänge zu stoppen, wenn der Client nicht mehr auf das Ergebnis wartet.

```csharp
router.MapGet("/connect", async (HttpRequest req) =>
{
    // bekommt den Disconnect‑Token aus der Anfrage
    var dc = req.DisconnectToken;

    await LongOperationAsync(dc);

    return new HttpResponse();
});
```

Dieser Token ist nicht mit allen HTTP‑Engines kompatibel, und jede erfordert eine eigene Implementierung.

Die Standard‑Sisk‑Engine, basierend auf `System.Net.HttpListener`, unterstützt keine Client‑Disconnect‑Erkennung. Wenn Ihre Anwendung die Standard‑Engine verwendet, ist `DisconnectToken` `CancellationToken.None`; praktisch handelt es sich um einen nicht abbruchfähigen Token, der als nicht verfügbar behandelt werden sollte.

Die [Cadente‑Engine](https://docs.sisk-framework.org/de/docs/cadente.md) unterstützt `DisconnectToken`. Wenn Ihre Route von einer disconnect‑bewussten Abbruch‑Logik abhängt, verwenden Sie Cadente oder eine andere Engine, die dieses Verhalten explizit implementiert. Auch bei einer unterstützten Engine ist das Abbrechen kooperativ: Geben Sie den Token an asynchrone APIs weiter und prüfen Sie ihn in Ihrer eigenen langlaufenden Arbeit.

## Unterstützung von Server‑Sent Events

Sisk unterstützt [Server‑Sent Events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events), wodurch Datenblöcke als Stream gesendet und die Verbindung zwischen Server und Client aufrechterhalten werden kann.

Der Aufruf der Methode [HttpRequest.GetEventSource](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpRequest.GetEventSource.md) versetzt das HttpRequest in den Listener‑Zustand. In diesem Zustand erwartet der Kontext dieser HTTP‑Anfrage keine HttpResponse, da sie die von Server‑Side‑Events gesendeten Pakete überlagern würde.

Nachdem alle Pakete gesendet wurden, muss der Callback die [Close](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpRequestEventSource.Close.md)‑Methode zurückgeben, die die finale Antwort an den Server sendet und das Ende des Streamings signalisiert.

Es ist nicht möglich, die Gesamtlänge aller zu sendenden Pakete vorherzusagen, sodass das Ende der Verbindung nicht über einen `Content‑Length`‑Header bestimmt werden kann.

Nach den Standardeinstellungen der meisten Browser unterstützen Server‑Side‑Events das Senden von HTTP‑Headern oder Methoden außer GET nicht. Seien Sie daher vorsichtig, wenn Sie Request‑Handler mit Event‑Source‑Anfragen verwenden, die spezifische Header benötigen, da diese wahrscheinlich nicht vorhanden sind.

Außerdem starten die meisten Browser Streams neu, wenn die Methode [EventSource.close](https://developer.mozilla.org/en-US/docs/Web/API/EventSource/close) auf der Client‑Seite nach Empfang aller Pakete nicht aufgerufen wird, was zu einer unendlichen zusätzlichen Verarbeitung auf Server‑Seite führt. Um dieses Problem zu vermeiden, ist es üblich, ein finales Paket zu senden, das anzeigt, dass die Event‑Source das Senden aller Pakete abgeschlossen hat.

Das folgende Beispiel zeigt, wie der Browser mit einem Server kommunizieren kann, der Server‑Side‑Events unterstützt.

```html {title="sse-example.html"}
<html>
    <body>
        <b>Fruits:</b>
        <ul></ul>
    </body>
    <script>
        const evtSource = new EventSource('http://localhost:5555/event-source');
        const eventList = document.querySelector('ul');
        
        evtSource.onmessage = (e) => {
            const newElement = document.createElement("li");

            newElement.textContent = `message: ${e.data}`;
            eventList.appendChild(newElement);

            if (e.data == "Tomato") {
                evtSource.close();
            }
        }
    </script>
</html>
```

Und das fortlaufende Senden der Nachrichten an den Client:

```cs {title="Controller/MyController.cs"}
public class MyController
{
    [RouteGet("/event-source")]
    public async Task<HttpResponse> ServerEventsResponse(HttpRequest request)
    {
        var serverEvents = await request.GetEventSourceAsync ();
        
        string[] fruits = new[] { "Apple", "Banana", "Watermelon", "Tomato" };
        
        foreach (string fruit in fruits)
        {
            await serverEvents.SendAsync(fruit);
            await Task.Delay(1500);
        }

        return await serverEvents.CloseAsync();
    }
}
```

Beim Ausführen dieses Codes erwarten wir ein Ergebnis, das diesem ähnelt:

<img src="/assets/img/server side events demo.gif" />

## Auflösen von proxied IPs und Hosts

Sisk kann mit Proxies verwendet werden, sodass IP‑Adressen durch den Proxy‑Endpunkt in der Transaktion vom Client zum Proxy ersetzt werden können.

Sie können eigene Resolver in Sisk mit [forwarding resolvers](https://docs.sisk-framework.org/de/docs/advanced/forwarding-resolvers.md) definieren.

## Header‑Kodierung

Header‑Kodierung kann bei einigen Implementierungen problematisch sein. Unter Windows werden UTF‑8‑Header nicht unterstützt, daher wird ASCII verwendet. Sisk verfügt über einen integrierten Kodierungskonverter, der beim Dekodieren falsch kodierter Header nützlich sein kann.

Dieser Vorgang ist kostenintensiv und standardmäßig deaktiviert, kann jedoch über [HttpServerConfiguration.NormalizeHeadersEncodings](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpServerConfiguration.NormalizeHeadersEncodings.md) aktiviert werden.
