10.5. API управления для камеры¶
Владельцу нужно настраивать чувствительность детектора движения откуда угодно – в ветреный день ветер сильнее раскачивает деревья. Это означает маршруты, из которых панель управления может читать текущие настройки и в которые может отправлять изменения.
Небольшого общего словаря состояния на уровне модуля достаточно, чтобы хранить настройки. На последующих страницах в него добавляются другие ключи; пока есть один:
state = {
'threshold': 12,
'frame_count': 0,
'trigger_count': 0,
}
10.5.1. GET для чтения, POST для записи¶
Пара маршрутов – один get, один post – даёт панели управления доступ к 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 возвращает тело, разобранное как JSON, или None, если Content-Type не был application/json. Обработчик post проходит по каждому режиму отказа – отсутствующий ключ, неверный тип, выход за пределы диапазона – и прерывается через microdot.abort(), который возбуждает microdot.HTTPException, чтобы досрочно завершить обработчик с заданным статусом и сообщением.
10.5.2. GET, POST, PUT, DELETE¶
get() и post() – те два, которые мы будем использовать чаще всего. put() и delete() существуют для случаев, следующих соглашениям REST – PUT /events/42 для замены события 42, DELETE /events/42 для его удаления. В остальном обработчик идентичен.
10.5.3. Чтение строк запроса и форм¶
Панель управления отправляет JSON, так что нам нужен request.json. Два других способа, которыми камера может получать данные:
args– строка запроса.?foo=1&bar=2становится объектомmicrodot.MultiDict, который можно читать черезrequest.args.get('foo').form– HTML-форма, отправленная какapplication/x-www-form-urlencoded. Тот же типMultiDict.
MultiDict похож на словарь, но позволяет одному ключу нести несколько значений (?tag=cat&tag=dog – это два значения tag); полный интерфейс см. в microdot.MultiDict.
10.5.4. Динамические сегменты URL¶
Путь маршрута может объявлять типизированные плейсхолдеры, которые microdot передаёт обработчику как дополнительные аргументы:
@app.get('/events/<int:event_id>')
async def get_event(request, event_id):
return {'id': event_id, 'msg': 'placeholder'}
Поддерживаемые преобразователи: <int:>, <re:> для произвольного регулярного выражения, <path:> для сегмента, который может содержать слеши, и значение по умолчанию (без префикса) для «совпадения с чем угодно вплоть до следующего слеша». <int:event_id> принимает /events/42 и отклоняет /events/abc – отклонение становится ответом 404 без запуска обработчика.
10.5.5. Пользовательские ответы об ошибках¶
По умолчанию microdot отправляет в ответе 404 обычный текст Not found. Панель управления ожидает JSON для каждого ответа; переопределите обработчик 404, чтобы он тоже возвращал JSON:
@app.errorhandler(404)
async def not_found(request):
return {'error': 'not found', 'path': request.path}, 404
errorhandler() принимает либо код статуса (перехватывает каждую ошибку с этим статусом), либо класс исключения (перехватывает каждый обработчик, возбудивший это исключение). Кортеж (body, status) досрочно формирует ответ без создания Response.
Теперь камера предоставляет своё состояние и принимает правки.