10.1. Ваш перший ендпоінт

Перш ніж камера зможе зробити щось корисне, решта мережі повинна мати можливість до неї підключитись. Найпростіший спосіб перевірити, що сервер працює, — це один HTTP-маршрут, який повертає деякий JSON:

from microdot import Microdot

app = Microdot()

frame_count = 0
trigger_count = 0

@app.get('/status')
async def status(request):
    return {'frames': frame_count, 'triggers': trigger_count}

app.run(host='0.0.0.0', port=80)

Запустіть скрипт в IDE. З будь-якої іншої машини в локальній мережі відкрийте http://<cam-ip>/status. Браузер покаже:

{"frames": 0, "triggers": 0}

Лічильники є заповнювачами — поки що їх ніщо не змінює — але запит пройшов через мережу, камера маршрутизувала його, виконала обробник і повернула JSON у відповідь.

10.1.1. Що робить кожен рядок

Один екземпляр microdot.Microdot на скрипт. Екземпляр керує таблицею маршрутизації, обробниками помилок і життєвим циклом (запуск, обслуговування, зупинка). Великі застосунки можуть розбиватись на кілька модулів Python, але всі вони використовують спільний об’єкт app.

@app.get('/status') — це декоратор маршруту. Тут ми використовуємо лише microdot.Microdot.get(); post(), put() та delete() з’являться на наступних сторінках, коли камера почне приймати записи.

Кожен обробник маршруту є asyncio-корутиною і отримує запит як перший аргумент. Обробнику не обов’язково використовувати request — цей ігнорує його — але параметр завжди присутній, щоб підпис був однаковим.

Повернення словника — найкоротший спосіб надіслати JSON. Microdot автоматично серіалізує словник у JSON і встановлює Content-Type: application/json у відповіді. Повернення рядка надсилає text/plain. Явне повернення microdot.Response — це повна форма, яка потрібна, коли тіло є двійковим або коли відповідь потребує власних заголовків.

app.run(host='0.0.0.0', port=80) запускає сервер. 0.0.0.0 означає слухати на кожному інтерфейсі камери — і на провідному Ethernet, і на Wi-Fi STA, якщо обидва активні. Порт 80 — це стандарт HTTP, тому браузерам не потрібно вводити номер порту.

10.1.2. Один запит від початку до кінця

The phone opens a TCP connection to the cam, sends an HTTP request, the cam parses, routes, runs the handler, then writes a response back.

Телефон відкриває TCP-з’єднання, записує рядок запиту та заголовки і чекає. Камера зчитує байти із сокета, розбирає їх у об’єкт microdot.Request, знаходить відповідний шлях і метод у таблиці маршрутизації, очікує виконання корутини обробника, серіалізує те, що вона повернула, записує рядок статусу, заголовки та тіло у зворотний сокет, потім закриває з’єднання (стандарт HTTP/1.0) або повторно використовує його (HTTP/1.1 з Connection: keep-alive). Весь обмін займає приблизно стільки часу, скільки потрібно на мережеве round-trip, плюс те, що виконував обробник.

10.1.3. Примітка щодо блокування

run() є блокуючим — він ніколи не повертається, поки сервер не зупиниться. Це прийнятно для однофункціонального сервера. Застосунок, який також захоплює кадри або виконує інші корутини, використовує замість нього start_server() всередині asyncio.run(), щоб HTTP-сервер міг ділити цикл подій з усім іншим.

Застосунок відповідає на одну URL-адресу.