10.1. Din första endpoint

Innan kameran kan göra något intressant måste resten av nätverket kunna nå den. Det billigaste sättet att bevisa att servern lever är en HTTP-endpoint med en enda rutt som returnerar lite JSON:

from microdot import Microdot

app = Microdot()

frame_count = 0
trigger_count = 0

@app.get('/status')
async def status(request):
    return {'frames': frame_count, 'triggers': trigger_count}

app.run(host='0.0.0.0', port=80)

Kör det i IDE:n. Öppna http://<cam-ip>/status från valfri annan maskin i det lokala nätverket. Webbläsaren visar:

{"frames": 0, "triggers": 0}

Räknarna är platshållare – ingenting rör dem ännu – men begäran tog sig över nätverket, kameran dirigerade den, körde en hanterare och skickade tillbaka JSON.

10.1.1. Vad varje rad gör

En microdot.Microdot-instans per skript. Instansen äger ruttabellen, felhanterarna och livscykeln (start, betjäning, stopp). Stora appar delas upp i flera Python-moduler men delar fortfarande ett enda app-objekt.

@app.get('/status') är ruttdekoratorn. Vi använder bara microdot.Microdot.get() här; post(), put() och delete() dyker upp på senare sidor när kameran börjar acceptera skrivningar.

Varje rutthanterare är en asyncio-coroutine och tar emot begäran som sitt första argument. Hanteraren behöver inte använda request – den här ignorerar det – men parametern finns alltid där så att signaturen är konsekvent.

Att returnera en dict är det kortaste sättet att skicka JSON. Microdot serialiserar dicten till JSON automatiskt och sätter Content-Type: application/json på svaret. Att returnera en sträng skickar text/plain. Att returnera en microdot.Response explicit är den långa formen – som behövs när kroppen är binär eller när svaret vill ha anpassade headers.

app.run(host='0.0.0.0', port=80) startar servern. 0.0.0.0 betyder lyssna på varje gränssnitt kameran har – både det kabelanslutna ethernet och wifi-STA:n, om båda är uppe. Port 80 är HTTP-standarden, så webbläsare behöver inte ange ett portnummer.

10.1.2. En begäran, från början till slut

Telefonen öppnar en TCP-anslutning till kameran, skickar en HTTP- begäran, kameran tolkar, dirigerar, kör hanteraren och skriver sedan tillbaka ett svar.

Telefonen öppnar en TCP-anslutning, skriver begäranraden och headers och väntar. Kameran läser byten från socketen, tolkar dem till ett microdot.Request-objekt, matchar sökvägen och metoden mot ruttabellen, inväntar hanterar-coroutinen, serialiserar vad den än returnerade, skriver tillbaka en statusrad, headers och kropp ner i socketen och stänger sedan anslutningen (HTTP/1.0-standard) eller återanvänder den (HTTP/1.1 med Connection: keep-alive). Hela utbytet tar ungefär så lång tid som nätverkets tur och retur plus vad hanteraren än gjorde.

10.1.3. En anmärkning om blockering

run() är blockerande – den returnerar aldrig förrän servern stoppas. Det är bra för en server med ett enda syfte. En app som också fångar bildrutor eller kör andra coroutiner använder start_server() inuti en asyncio.run() istället, så att HTTP-servern kan dela loopen med allt annat.

Appen svarar på en URL.