microdot.sse — Server-Sent Events

Server-Sent Events (SSE) ist ein einfaches Einweg-Push-Protokoll: Der Client öffnet einen normalen HTTP-Request an Content-Type: text/event-stream, und der Server hält die Verbindung offen und schreibt formatierte Ereignisse, sobald sie auftreten. Der Browser stellt den Stream als JavaScript-EventSource-Objekt bereit. Im Vergleich zu WebSockets ist SSE einfacher, wenn nur der Server pusht; der Client fordert immer an.

Die typische Verwendung bei Kameras ist die Veröffentlichung von Erkennungen / Sensormesswerten / Statusaktualisierungen an ein Telefon oder Dashboard in der Geschwindigkeit, in der sie auftreten, ohne dass das Dashboard pollen muss.

class SSE

class microdot.sse.SSE

Das Handle, das die Route erhält. Wird Handlern als zweites Argument übergeben, wenn with_sse() verwendet wird.

async send(data, event: str | None = None, event_id: str | None = None, retry: int | None = None, comment: bool = False)

Pusht ein einzelnes Ereignis an den Client.

data

Die Ereignis-Nutzlast. Strings und Bytes werden unverändert gesendet. Dicts und Listen werden zu JSON serialisiert. Alles andere wird mit str() umgewandelt.

event

Optionaler Ereignisname – die EventSource des Browsers leitet an einen JavaScript-Listener desselben Namens weiter, wenn dies gesetzt ist.

event_id

Optionale Ereignis-ID – der Browser sendet sie als Last-Event-ID zurück, falls die Verbindung abbricht und wiederhergestellt wird, was es dem Server ermöglicht, dort fortzufahren, wo er aufgehört hat.

retry

Wiederverbindungsverzögerung in Sekunden; der Browser verwendet diese, falls die Verbindung abbricht.

comment

Wenn True, wird die Nutzlast als SSE-Kommentarzeile gesendet (vom Browser ignoriert). Nützlich als Keep-Alive-Heartbeat, um zu verhindern, dass NAT-Timeouts einen ansonsten untätigen Stream schließen.

Decorators auf Modulebene

microdot.sse.with_sse(f)

Decorator, der eine Route in einen SSE-Endpunkt verwandelt. Der Handler erhält den Request und ein SSE-Objekt:

from microdot import Microdot
from microdot.sse import with_sse

app = Microdot()

@app.get('/events')
@with_sse
async def events(request, sse):
    import asyncio
    while True:
        await sse.send({'temp': read_sensor()}, event='reading')
        await asyncio.sleep(1)

Die Lebensdauer des Handlers entspricht der Lebensdauer des Streams – er läuft so lange, wie der Client verbunden bleibt. Eine Abbruchanforderung (der Client trennt die Verbindung, der Server fährt herunter) propagiert als asyncio.CancelledError, die das Framework verschluckt.

microdot.sse.sse_response(request, event_function, *args, **kwargs)

Low-Level-Einstiegspunkt: gibt das Response-Tupel zurück, das with_sse() unter der Haube zurückgibt. Nützlich, wenn ein SSE-Endpunkt benutzerdefinierte Header oder Statuscodes benötigt, die der Decorator nicht zulässt:

from microdot.sse import sse_response

@app.get('/events')
async def events(request):
    if not request.g.current_user:
        return 401
    async def emit(req, sse):
        await sse.send('hello')
    return sse_response(request, emit)

event_function nimmt (request, sse, *args, **kwargs) entgegen und verwendet await sse.send(...), um Ereignisse zu pushen.

Die SSE-Antwort setzt Content-Type: text/event-stream und hält die Verbindung offen, bis event_function zurückkehrt oder der Client die Verbindung trennt. Verwenden Sie eine while True-Schleife mit einem await asyncio.sleep zwischen den Sends, um eine Hot-Loop zu vermeiden.