10.5. Un’API di controllo per la camera¶
Il proprietario ha bisogno di impostare la sensibilità del rilevatore di movimento da qualunque posizione: nelle giornate ventose il vento muove di più gli alberi. Questo richiede delle route da cui la dashboard possa leggere le impostazioni correnti e a cui inviare modifiche.
Un piccolo dict di stato condiviso a livello di modulo è sufficiente a contenere i parametri. Le pagine successive vi aggiungono altre chiavi; per ora ce n’è una:
state = {
'threshold': 12,
'frame_count': 0,
'trigger_count': 0,
}
10.5.1. GET per leggere, POST per scrivere¶
Una coppia di route – una get, una post – offre alla dashboard accesso in lettura/scrittura a 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 restituisce il corpo interpretato come JSON, oppure None se il Content-Type non era application/json. L’handler post percorre ogni modalità di errore – chiave mancante, tipo errato, valore fuori intervallo – e si interrompe con microdot.abort(), che solleva microdot.HTTPException per interrompere immediatamente l’handler con lo stato e il messaggio indicati.
10.5.2. GET, POST, PUT, DELETE¶
get() e post() sono i due che useremo più spesso. put() e delete() esistono per i casi che seguono le convenzioni REST: un PUT /events/42 per sostituire l’evento 42, un DELETE /events/42 per eliminarlo. Per il resto l’handler è identico.
10.5.3. Lettura di query string e form¶
La dashboard invia JSON, quindi request.json è ciò che ci serve. Altri due modi in cui la camera potrebbe ricevere dati:
args– la query string.?foo=1&bar=2diventa unmicrodot.MultiDictche puoi leggere conrequest.args.get('foo').form– un form HTML inviato comeapplication/x-www-form-urlencoded. Lo stesso tipoMultiDict.
Il MultiDict è simile a un dict, ma consente a una stessa chiave di contenere più valori (?tag=cat&tag=dog sono due valori tag); consulta microdot.MultiDict per l’interfaccia completa.
10.5.4. Segmenti di URL dinamici¶
Il percorso di una route può dichiarare segnaposto tipizzati che microdot passa all’handler come argomenti aggiuntivi:
@app.get('/events/<int:event_id>')
async def get_event(request, event_id):
return {'id': event_id, 'msg': 'placeholder'}
I convertitori supportati sono <int:>, <re:> per una regex personalizzata, <path:> per un segmento che può contenere slash, e quello predefinito (nessun prefisso) per «corrisponde a qualsiasi cosa fino allo slash successivo». <int:event_id> accetta /events/42 e rifiuta /events/abc: il rifiuto si traduce in un 404 senza che l’handler venga eseguito.
10.5.5. Risposte di errore personalizzate¶
Il 404 predefinito che microdot invia è un semplice Not found. La dashboard si aspetta JSON per ogni risposta; sovrascrivi l’handler del 404 in modo che restituisca anch’esso JSON:
@app.errorhandler(404)
async def not_found(request):
return {'error': 'not found', 'path': request.path}, 404
errorhandler() accetta o un codice di stato (intercetta ogni errore con quello stato) o una classe di eccezione (intercetta ogni handler che ha sollevato quell’eccezione). La tupla (body, status) produce la risposta immediatamente senza costruire un oggetto Response.
La camera ora espone il proprio stato e accetta modifiche.