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. Один запит від початку до кінця¶
Телефон відкриває TCP-з’єднання, записує рядок запиту та заголовки і чекає. Камера зчитує байти із сокета, розбирає їх у об’єкт microdot.Request, знаходить відповідний шлях і метод у таблиці маршрутизації, очікує виконання корутини обробника, серіалізує те, що вона повернула, записує рядок статусу, заголовки та тіло у зворотний сокет, потім закриває з’єднання (стандарт HTTP/1.0) або повторно використовує його (HTTP/1.1 з Connection: keep-alive). Весь обмін займає приблизно стільки часу, скільки потрібно на мережеве round-trip, плюс те, що виконував обробник.
10.1.3. Примітка щодо блокування¶
run() є блокуючим — він ніколи не повертається, поки сервер не зупиниться. Це прийнятно для однофункціонального сервера. Застосунок, який також захоплює кадри або виконує інші корутини, використовує замість нього start_server() всередині asyncio.run(), щоб HTTP-сервер міг ділити цикл подій з усім іншим.
Застосунок відповідає на одну URL-адресу.