Работа с API: нода HTTP Request
Введение в API и протокол HTTP
В современной автоматизации изолированные системы малоэффективны. Ключом к созданию по-настоящему интеллектуальных объектов является интеграция — способность контроллера обмениваться данными с внешними сервисами, облачными платформами и оборудованием других производителей. Именно здесь на сцену выходит API (Application Programming Interface) — программный интерфейс приложения.
Простыми словами, API — это набор правил и определений, по которым одна компьютерная программа может взаимодействовать с другой. Это своего рода "контракт", описывающий, какие запросы можно отправлять, какие данные нужно для этого предоставить и в каком формате придет ответ. Представьте API как меню в ресторане: оно говорит вам, какие блюда (данные) вы можете заказать (запросить), какие ингредиенты (параметры) указать, и что вы получите в итоге.
> 📋 Ключевые понятия:
> * API: Контракт, который позволяет программным системам общаться друг с другом без необходимости знать внутреннее устройство каждой из них.
> * Клиент: Программа, которая инициирует запрос (например, наш контроллер HI).
> * Сервер: Программа, которая принимает запрос, обрабатывает его и возвращает ответ (например, погодный сервис или облачная платформа производителя оборудования).
Сравнение подходов: MQTT и HTTP
В предыдущих уроках мы подробно изучали протокол MQTT, который основан на модели Издатель-Подписчик (Publish/Subscribe). Протокол HTTP (HyperText Transfer Protocol), лежащий в основе большинства современных API, работает по другой модели — Клиент-Сервер. Понимание их различий критически важно для архитектора автоматизации.
| Характеристика | MQTT (Publish/Subscribe) | HTTP (Клиент-Сервер) |
| :--- | :--- | :--- |
| Модель связи | Асинхронная, через брокера. Издатель и подписчик не знают друг о друге. | Синхронная, прямая. Клиент отправляет запрос и ждет ответа от сервера. |
| Состояние | Сохранение состояния. Брокер может хранить "последнее известное значение" (retained messages). | Без сохранения состояния (Stateless). Каждый запрос является независимым. |
| Инициатор | Данные отправляются по событию (например, изменение показаний датчика). | Данные передаются по запросу клиента. |
| Трафик | Минимальный, легкие заголовки. Идеально для слабых каналов связи. | Более "тяжелый", объемные заголовки. Требует более стабильного соединения. |
| Типичный сценарий | Внутренняя шина данных на объекте, телеметрия, мгновенные команды M2M. | Интеграция с внешними облачными сервисами, веб-сайтами, системами управления. |
Основы HTTP
Взаимодействие по HTTP состоит из двух основных частей: запроса (request) от клиента и ответа (response) от сервера.
HTTP-запрос
* GET: Запросить данные с сервера. Не изменяет состояние сервера. Например, получить текущую погоду.
* POST: Отправить данные на сервер для создания новой сущности или выполнения действия. Например, отправить команду на включение света.
* PUT: Полностью обновить существующую сущность на сервере.
* DELETE: Удалить сущность на сервере.
HTTP-ответ
Ключевые коды состояния HTTP
Умение читать и правильно интерпретировать коды состояния — базовый навык при работе с API.
- `200 OK`: Запрос успешно выполнен. Ответ содержит запрошенные данные (для GET).
- `201 Created`: Запрос успешно выполнен, и в результате был создан новый ресурс (для POST/PUT).
- `400 Bad Request`: Сервер не смог понять запрос из-за неверного синтаксиса (например, отправлен невалидный JSON).
- `401 Unauthorized`: Ошибка аутентификации. Либо не предоставлены учетные данные, либо они неверны.
- `404 Not Found`: Запрошенный ресурс не найден на сервере. Вы "постучались" в несуществующую дверь.
- `500 Internal Server Error`: Внутренняя ошибка на сервере. Наш запрос был корректен, но на стороне сервера что-то пошло не так.
---
Обзор ноды HTTP Request
Нода HTTP Request является стандартным и мощным инструментом в палитре Node-RED для взаимодействия с любыми веб-сервисами и REST API. Она инкапсулирует всю сложность формирования HTTP-запросов, позволяя инженеру сосредоточиться на логике.
> ⚠️ Внимание: Никогда не храните токены доступа, логины и пароли в открытом виде в узлах `Function` или `Change`. Всегда используйте встроенный механизм Credentials для безопасности. Экспортируя поток, вы рискуете опубликовать свои секретные ключи.
Детальный разбор панели настроек
При двойном клике на ноду открывается панель её конфигурации с ключевыми параметрами:
* a parsed JSON object: Самый частый выбор. Node-RED попытается автоматически преобразовать текстовый ответ (если он в формате JSON) в нативный JavaScript-объект, который сразу же готов к использованию в `msg.payload`.
* a UTF-8 string: Ответ будет помещен в `msg.payload` как есть, в виде строки. Полезно для получения HTML-страниц или текстовых файлов.
* a binary buffer: Ответ будет представлен в виде буфера двоичных данных. Необходимо при скачивании файлов, изображений, аудио.
* Basic authentication: Использует пару логин/пароль.
* Bearer authentication: Наиболее распространенный метод для современных API, использующий токен доступа (bearer token).
Динамическая конфигурация ноды
Жестко задавать все параметры в настройках ноды — плохая практика, так как это снижает гибкость потока. Нода `HTTP Request` позволяет переопределить большинство своих настроек через свойства входящего объекта `msg`.
| Свойство в `msg` | Переопределяемый параметр ноды | Пример значения |
| :--- | :--- | :--- |
| `msg.method` | Method | `"POST"` |
| `msg.url` | URL | `"https://api.example.com/v1/devices"` |
| `msg.headers` | HTTP Headers | `{ "Content-Type": "application/json" }` |
| `msg.payload` | Request Body | `{ "command": "turn_on", "id": "123" }` |
| `msg.cookies` | Cookies | `{ "session_id": "xyz789" }` |
Этот механизм позволяет создавать универсальные потоки. Например, можно создать один поток для управления несколькими однотипными устройствами, передавая конкретный URL и команду во входящем `msg`.
Безопасное хранение учетных данных
Рассмотрим правильный способ работы с секретными данными, например, с токеном доступа для API.
---
Практика: GET-запрос для получения данных
Давайте создадим поток, который будет получать актуальный прогноз погоды для заданного города, используя публичное API. Мы будем использовать сервис, который предоставляет данные в формате JSON.
> 💡 Подсказка: Используйте онлайн-инструменты для форматирования JSON (JSON formatter), чтобы визуально разобрать структуру ответа API и правильно составить путь для извлечения данных. Это значительно упрощает отладку.
Пошаговая инструкция
Задача: Каждые 15 минут запрашивать погоду для Москвы и извлекать из ответа температуру и влажность, приводя их к стандартному контракту сообщения.* `Payload`: `timestamp` (по умолчанию).
* `Repeat`: `interval` every `15` `minutes`.
* `Name`: "Запрос погоды (15 мин)".
* `Name`: "Set URL & API Key".
* Установите правило: `Set` `msg.url` `to` `https://api.openweathermap.org/data/2.5/weather?q=Moscow&appid=ВАШ_КЛЮЧ_API&units=metric`
* ℹ️ Информация: Замените `ВАШ_КЛЮЧ_API` на реальный ключ, полученный при регистрации на сервисе OpenWeatherMap. Параметр `units=metric` указывает, что мы хотим получить температуру в градусах Цельсия.
* `Method`: `GET` (установлен по умолчанию).
* `URL`: оставьте это поле пустым, так как мы задаем его динамически в `msg.url`.
* `Return`: `a parsed JSON object`. Это критически важно, чтобы Node-RED автоматически превратил ответ в удобный для работы объект.
* `Name`: "GET Weather API".
* `Output`: `complete msg object`. Это позволит нам увидеть не только `msg.payload`, но и `msg.statusCode` и `msg.headers` ответа.
{
"coord": { "lon": 37.6156, "lat": 55.7522 },
"weather": [
{
"id": 800,
"main": "Clear",
"description": "clear sky",
"icon": "01d"
}
],
"base": "stations",
"main": {
"temp": 22.5,
"feels_like": 22.1,
"temp_min": 21.0,
"temp_max": 23.5,
"pressure": 1012,
"humidity": 45
},
"visibility": 10000,
"wind": { "speed": 4, "deg": 270 },
"clouds": { "all": 0 },
"dt": 1656070275,
"sys": {
"type": 2,
"id": 2000314,
"country": "RU",
"sunrise": 1656035251,
"sunset": 1656099194
},
"timezone": 10800,
"id": 524901,
"name": "Moscow",
"cod": 200
}
Извлечение данных с помощью JSONata
Теперь наша задача — извлечь из этого сложного объекта только нужные нам поля (`temp` и `humidity`) и упаковать их в наш стандартный формат сообщения. Для этого добавим еще один узел `Change` после `HTTP Request`.
* `Name`: "Format Weather Data".
* Создайте два правила:
1. Правило 1 (Температура):
* `Set`: `msg.payload.temperature`
* `to the value`: `payload.main.temp`
* Важно: измените тип ввода со строки на `J: (expression)` (JSONata). This tells Node-RED to interpret the value as a path within the object.
2. Правило 2 (Влажность):
* `Set`: `msg.payload.humidity`
* `to the value`: `payload.main.humidity`
* Тип ввода: `J: (expression)` (JSONata).
После выполнения потока на выходе этого узла `Change` объект `msg.payload` будет иметь гораздо более простую и полезную для нас структуру:
{
"temperature": 22.5,
"humidity": 45
}
Это сообщение уже можно легко отправить в MQTT, сохранить в базу данных MySQL или использовать в дальнейшей логике автоматизации.
---
Практика: POST-запрос для управления устройством
Теперь рассмотрим сценарий, в котором мы не получаем, а отправляем данные, чтобы изменить состояние устройства.
Сценарий: Наш контроллер HI должен управлять умной розеткой стороннего производителя, у которой есть локальный REST API. Для включения розетки необходимо отправить POST-запрос на `http://192.168.1.123/api/relay/0` с телом `{"state": "on"}`.Пошаговая инструкция
* Первый `Inject`: `Payload` (string) = `ON`, `Topic` (string) = `cmnd/light/on`. `Name`: "Включить".
* Второй `Inject`: `Payload` (string) = `OFF`, `Topic` (string) = `cmnd/light/off`. `Name`: "Выключить".
* `Name`: "Prepare POST Request".
* Добавьте 4 правила для формирования полного запроса:
1. `Set` `msg.method` `to` `POST`.
2. `Set` `msg.url` `to` `http://192.168.1.123/api/relay/0`.
3. `Set` `msg.headers` `to` `J: (expression)` `{ "Content-Type": "application/json" }`. Это критически важно! Заголовок `Content-Type` сообщает серверу, что мы отправляем данные в формате JSON.
4. `Set` `msg.payload` `to` `J: (expression)`
payload = "ON" ? {"state": "on"} : {"state": "off"}
Эта JSONata-конструкция является тернарным оператором. Она проверяет: если `msg.payload` равен "ON", то создать объект `{"state": "on"}`, в противном случае — `{"state": "off"}`.
* `Method`: Оставьте `не задано`, так как мы передаем его в `msg.method`.
* `URL`: Оставьте пустым.
* `Return`: `a UTF-8 string` (или `a parsed JSON object`, если API возвращает JSON-ответ).
* `Name`: "POST to Smart Socket".
После нажатия на один из узлов `Inject` будет сформирован и отправлен полный POST-запрос. `HTTP Request` выполнит действие, и (если API что-то возвращает) мы увидим ответ в узле `Debug`, подключенном после него.
---
Обработка ответов и ошибок
Отправить запрос — это только полдела. Профессионально построенный поток должен уметь правильно интерпретировать ответ сервера и корректно обрабатывать как успешные сценарии, так и ошибки.
> 🔗 Связанный материал: Для коммуникаций внутри объекта, например, между контроллерами HI, предпочтительнее использовать MQTT из-за его легковесности и асинхронной природы. Этот подход детально рассмотрен в уроке `COURSE-06-M08-L04: Практика: Связывание двух контроллеров по MQTT`.
После выполнения запроса нода `HTTP Request` обогащает объект `msg` несколькими ключевыми свойствами:
- `msg.payload`: Тело ответа (в том формате, который вы выбрали в настройках, например, JSON-объект).
- `msg.statusCode`: Числовой код состояния HTTP (например, `200`, `404`, `500`). Это главный индикатор успеха или провала операции.
- `msg.headers`: Объект с заголовками ответа сервера.
Маршрутизация на основе кода ответа
Самый надежный способ построить логику обработки — использовать узел `Switch` после ноды `HTTP Request`.
Пример:* Выход 1 (Успех): `is between` `200` and `299`. Сюда попадут все успешные ответы (`200 OK`, `201 Created` и т.д.). Эту ветку можно направить на дальнейшую обработку данных.
* Выход 2 (Ошибка клиента): `is between` `400` and `499`. Здесь мы обрабатываем такие ошибки, как `401 Unauthorized` (неверный токен) или `400 Bad Request` (мы отправили что-то не то).
* Выход 3 (Ошибка сервера): `is between` `500` and `599`. Означает, что проблема на стороне API.
* Выход 4 (Другое): `otherwise`. Для отлова всех остальных непредвиденных кодов.
Стратегии обработки ошибок
- Ветка "Успех" (2xx): Продолжаем обработку `msg.payload`, как в примере с погодой. Можно обновить статус устройства в Node-RED с помощью `node.status({fill:"green", ...})`.
- Ветка "Ошибка клиента" (4xx): Эти ошибки обычно требуют вмешательства инженера.
* Действие: Отправить уведомление администратору (через MQTT, Telegram и т.д.) с указанием `msg.statusCode` и, возможно, `msg.payload` (где сервер часто описывает причину ошибки).
* Пример: `node.error("Client Error: " + msg.statusCode, msg);`
- Ветка "Ошибка сервера" (5xx): Эти ошибки могут быть временными. Здесь уместно реализовать логику повторных попыток.
* Действие: После нескольких неудачных попыток (например, 3 раза), поток должен прекратить попытки и отправить критическое уведомление. Для подсчета попыток удобно использовать контекст потока (`flow context`).
---
Резюме и лучшие практики
Интеграция через HTTP API открывает контроллеру HI доступ к безграничному миру внешних данных и сервисов. Правильное применение ноды `HTTP Request` является ключевым навыком для инженера по автоматизации.
Краткое повторение
- GET: "Дай мне данные". Используется для чтения информации. Безопасен и идемпотентен (многократный вызов дает тот же результат).
- POST: "Прими эти данные и сделай что-то". Используется для создания новых записей или выполнения команд. Не является идемпотентным.
Чек-лист для отладки API-запросов
Если ваш HTTP-запрос не работает, последовательно проверьте:
Безопасность и выбор протокола
> ⚠️ Внимание: Всегда используйте HTTPS вместо HTTP при работе с внешними сервисами через интернет. HTTPS шифрует весь трафик между вашим контроллером и сервером API, защищая передаваемые данные (включая токены и пароли) от перехвата.
Когда выбирать HTTP API, а когда — MQTT?- HTTP API: Используйте для интеграции с внешними, сторонними системами (облачные сервисы, погодные API, мессенджеры). Это модель "запрос-ответ", идеальная, когда вам нужны данные по требованию.
- MQTT: Используйте как основную шину данных внутри вашего объекта автоматизации. Это событийно-ориентированная модель, идеальная для мгновенной передачи телеметрии от датчиков и команд исполнительным устройствам в реальном времени.
Что дальше
В этом уроке мы освоили клиент-серверное взаимодействие по протоколу HTTP и научились интегрировать наш контроллер с внешними API. В следующем уроке мы рассмотрим обратную задачу: как превратить сам контроллер HI в HTTP-сервер с помощью нод `HTTP In` и `HTTP Response`, чтобы он мог принимать команды и запросы от других систем в вашей локальной сети.