10.5. Un API de control pentru cameră#

Proprietarul are nevoie să seteze sensibilitatea detectorului de mișcare de oriunde – vântul mișcă mai mult copacii într-o zi cu vânt. Asta înseamnă rute din care tabloul de bord poate citi setările curente și către care poate trimite modificări.

Un mic dicționar de stare partajat pe modul este suficient pentru a păstra reglajele. Paginile ulterioare adaugă mai multe chei la el; deocamdată există una singură:

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

10.5.1. GET pentru citire, POST pentru scriere#

O pereche de rute – una get, una post – oferă tabloului de bord acces de citire/scriere la 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 returnează corpul analizat ca JSON, sau None dacă Content-Type nu a fost application/json. Handlerul post parcurge fiecare mod de eșec – cheie lipsă, tip greșit, valoare în afara intervalului – și abandonează cu microdot.abort(), care ridică microdot.HTTPException pentru a scurtcircuita handlerul cu statusul și mesajul date.

10.5.2. GET, POST, PUT, DELETE#

get() și post() sunt cele două pe care le vom folosi cel mai mult. put() și delete() există pentru cazurile care urmează convențiile REST – un PUT /events/42 pentru a înlocui evenimentul 42, un DELETE /events/42 pentru a-l elimina. Handlerul este în rest identic.

10.5.3. Citirea șirurilor de interogare și a formularelor#

Tabloul de bord trimite JSON, așa că request.json este ceea ce dorim. Alte două moduri în care camera ar putea primi date:

  • args – șirul de interogare. ?foo=1&bar=2 devine un microdot.MultiDict pe care îl poți citi cu request.args.get('foo').

  • form – un formular HTML trimis ca application/x-www-form-urlencoded. Același tip MultiDict.

MultiDict este asemănător unui dicționar, dar permite unei chei să poarte mai multe valori (?tag=cat&tag=dog reprezintă două valori tag); vezi microdot.MultiDict pentru suprafața completă.

10.5.4. Segmente de URL dinamice#

O cale de rută poate declara substituenți tipizați pe care microdot îi transmite handlerului ca argumente suplimentare:

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

Convertoarele acceptate sunt <int:>, <re:> pentru o expresie regulată personalizată, <path:> pentru un segment care poate conține bare oblice și cel implicit (fără prefix) pentru „potrivește orice până la următoarea bară oblică.” <int:event_id> acceptă /events/42 și respinge /events/abc – respingerea devine un 404 fără ca handlerul să ruleze.

10.5.5. Răspunsuri de eroare personalizate#

Răspunsul 404 implicit pe care îl trimite microdot este pur și simplu Not found. Tabloul de bord așteaptă JSON pentru fiecare răspuns; suprascrie handlerul de 404 astfel încât să returneze și el JSON:

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

errorhandler() acceptă fie un cod de status (prinde fiecare eroare cu acel status), fie o clasă de excepție (prinde fiecare handler care a ridicat acea excepție). Tuplul (body, status) scurtcircuitează răspunsul fără a construi un Response.

Camera își expune acum starea și acceptă modificări.