10.5. API sterujące dla kamery

Właściciel musi móc ustawiać czułość detektora ruchu z dowolnego miejsca – w wietrzny dzień drzewa poruszają się bardziej. Oznacza to trasy, z których panel może odczytać bieżące ustawienia i do których może wysłać zmiany.

Mały współdzielony słownik stanu w module wystarcza, by przechowywać pokrętła. Późniejsze strony dodają do niego więcej kluczy; na razie jest jeden:

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

10.5.1. GET do odczytu, POST do zapisu

Para tras – jedna get, jedna post – daje panelowi dostęp do odczytu i zapisu 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 zwraca treść sparsowaną jako JSON lub None, jeśli Content-Type nie był application/json. Handler post przechodzi przez każdy tryb awarii – brakujący klucz, zły typ, poza zakresem – i przerywa za pomocą microdot.abort(), które zgłasza microdot.HTTPException, aby zakończyć handler na skróty z podanym statusem i komunikatem.

10.5.2. GET, POST, PUT, DELETE

get() i post() to dwie, których będziemy używać najczęściej. put() oraz delete() istnieją dla przypadków zgodnych z konwencjami REST – PUT /events/42 zastępujące zdarzenie 42, DELETE /events/42 usuwające je. Poza tym handler jest identyczny.

10.5.3. Odczytywanie ciągów zapytań i formularzy

Panel wysyła JSON, więc request.json to to, czego chcemy. Dwa inne sposoby, na jakie kamera może odbierać dane:

  • args – ciąg zapytania. ?foo=1&bar=2 staje się obiektem microdot.MultiDict, który możesz odczytać przez request.args.get('foo').

  • form – formularz HTML wysłany jako application/x-www-form-urlencoded. Ten sam typ MultiDict.

MultiDict jest słownikopodobny, ale pozwala, by jeden klucz niósł wiele wartości (?tag=cat&tag=dog to dwie wartości tag); pełny interfejs znajdziesz w microdot.MultiDict.

10.5.4. Dynamiczne segmenty URL

Ścieżka trasy może deklarować typowane symbole zastępcze, które microdot przekazuje do handlera jako dodatkowe argumenty:

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

Obsługiwane konwertery to <int:>, <re:> dla niestandardowego wyrażenia regularnego, <path:> dla segmentu mogącego zawierać ukośniki oraz domyślny (bez prefiksu) dla „dopasuj cokolwiek aż do następnego ukośnika”. <int:event_id> akceptuje /events/42 i odrzuca /events/abc – odrzucenie staje się błędem 404 bez uruchamiania handlera.

10.5.5. Niestandardowe odpowiedzi błędów

Domyślny błąd 404 wysyłany przez microdot to zwykłe Not found. Panel oczekuje JSON-a dla każdej odpowiedzi; nadpisz handler 404, aby on także zwracał JSON:

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

errorhandler() przyjmuje albo kod statusu (przechwytuje każdy błąd o tym statusie), albo klasę wyjątku (przechwytuje każdy handler, który zgłosił ten wyjątek). Krotka (body, status) zwraca odpowiedź na skróty bez konstruowania Response.

Kamera udostępnia teraz swój stan i przyjmuje edycje.