10.5. Een besturings-API voor de cam#

De eigenaar moet de gevoeligheid van de bewegingsdetector overal vandaan kunnen instellen – de wind beweegt bomen meer op een winderige dag. Dat betekent routes waaruit het dashboard de huidige instellingen kan lezen en waaraan het wijzigingen kan posten.

Een kleine gedeelde state-dict op de module is genoeg om de knoppen te bevatten. Latere pagina’s voegen er meer sleutels aan toe; voorlopig is er één:

state = {
    'threshold': 12,
    'frame_count': 0,
    'trigger_count': 0,
}

10.5.1. GET om te lezen, POST om te schrijven#

Een paar routes – één get en één post – geeft het dashboard lees- en schrijftoegang tot state:

from microdot import abort

@app.get('/config')
async def get_config(request):
    return state

@app.post('/config')
async def set_config(request):
    body = request.json
    if not body or 'threshold' not in body:
        abort(400, 'missing threshold')
    try:
        threshold = int(body['threshold'])
    except (TypeError, ValueError):
        abort(400, 'threshold must be an integer')
    if not 0 <= threshold <= 100:
        abort(400, 'threshold out of range')
    state['threshold'] = threshold
    return {'ok': True, 'threshold': threshold}

microdot.Request.json geeft de body terug, geparseerd als JSON, of None als Content-Type niet application/json was. De post-handler loopt elke faalmodus af – ontbrekende sleutel, verkeerd type, buiten bereik – en breekt af met microdot.abort(), die een microdot.HTTPException opwerpt om de handler kort te sluiten met de opgegeven status en melding.

10.5.2. GET, POST, PUT, DELETE#

get() en post() zijn de twee die we het meest zullen gebruiken. put() en delete() bestaan voor gevallen die de REST-conventies volgen – een PUT /events/42 om event 42 te vervangen, een DELETE /events/42 om het te verwijderen. De handler is verder identiek.

10.5.3. Query strings en formulieren lezen#

Het dashboard post JSON, dus request.json is wat we willen. Twee andere manieren waarop de cam gegevens kan ontvangen:

  • args – de query string. ?foo=1&bar=2 wordt een microdot.MultiDict die je kunt lezen met request.args.get('foo').

  • form – een HTML-formulier dat als application/x-www-form-urlencoded is gepost. Hetzelfde MultiDict-type.

De MultiDict is dict-achtig, maar laat één sleutel meerdere waarden dragen (?tag=cat&tag=dog zijn twee tag-waarden); zie microdot.MultiDict voor het volledige oppervlak.

10.5.4. Dynamische URL-segmenten#

Een routepad kan getypeerde plaatshouders declareren die microdot als extra argumenten aan de handler doorgeeft:

@app.get('/events/<int:event_id>')
async def get_event(request, event_id):
    return {'id': event_id, 'msg': 'placeholder'}

De ondersteunde converters zijn <int:>, <re:> voor een aangepaste regex, <path:> voor een segment dat schuine strepen kan bevatten, en de standaard (geen prefix) voor “match alles tot de volgende schuine streep.” <int:event_id> accepteert /events/42 en weigert /events/abc – de weigering wordt een 404 zonder dat de handler draait.

10.5.5. Aangepaste foutresponses#

De standaard 404 die microdot stuurt is gewoon Not found. Het dashboard verwacht JSON voor elke response; overschrijf de 404-handler zodat hij ook JSON teruggeeft:

@app.errorhandler(404)
async def not_found(request):
    return {'error': 'not found', 'path': request.path}, 404

errorhandler() neemt ofwel een statuscode (vangt elke fout van die status) ofwel een uitzonderingsklasse (vangt elke handler die die uitzondering opwierp). De (body, status)-tuple sluit de response kort zonder een Response te construeren.

De cam stelt nu zijn state beschikbaar en accepteert wijzigingen.