ГлавнаяАкадемияNode-RED: установка, flows, msg/JSON, отладка → Работа с API: нода HTTP Request

Работа с API: нода HTTP Request

Урок 4 · Node-RED: установка, flows, msg/JSON, отладка · 30 мин · theory

Введение в 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-запрос

  • Метод (Method): Определяет действие, которое клиент хочет выполнить. Наиболее важные для нас:
  • * GET: Запросить данные с сервера. Не изменяет состояние сервера. Например, получить текущую погоду.

    * POST: Отправить данные на сервер для создания новой сущности или выполнения действия. Например, отправить команду на включение света.

    * PUT: Полностью обновить существующую сущность на сервере.

    * DELETE: Удалить сущность на сервере.

  • URL (Uniform Resource Locator): Адрес ресурса, к которому мы обращаемся (например, `https://api.weather.com/v1/forecast`).
  • Заголовки (Headers): Метаданные запроса. Содержат служебную информацию, например, тип контента (`Content-Type: application/json`) или данные для авторизации (`Authorization: Bearer <ваш_токен>`).
  • Тело (Body / Payload): Непосредственно данные, которые мы отправляем на сервер. Используется в методах `POST` и `PUT`. В `GET` запросах тело обычно отсутствует.
  • HTTP-ответ

  • Код состояния (Status Code): Трехзначное число, указывающее на результат обработки запроса. Критически важно для обработки ошибок.
  • Заголовки (Headers): Метаданные ответа. Могут содержать информацию о типе контента, ограничении на количество запросов и т.д.
  • Тело (Body / Payload): Данные, которые сервер вернул в ответ на наш запрос. Чаще всего это JSON (JavaScript Object Notation) — текстовый формат обмена данными, который легко читается и человеком, и машиной.
  • Ключевые коды состояния HTTP

    Умение читать и правильно интерпретировать коды состояния — базовый навык при работе с API.

    ---

    Обзор ноды HTTP Request

    Нода HTTP Request является стандартным и мощным инструментом в палитре Node-RED для взаимодействия с любыми веб-сервисами и REST API. Она инкапсулирует всю сложность формирования HTTP-запросов, позволяя инженеру сосредоточиться на логике.

    > ⚠️ Внимание: Никогда не храните токены доступа, логины и пароли в открытом виде в узлах `Function` или `Change`. Всегда используйте встроенный механизм Credentials для безопасности. Экспортируя поток, вы рискуете опубликовать свои секретные ключи.

    Детальный разбор панели настроек

    При двойном клике на ноду открывается панель её конфигурации с ключевыми параметрами:

  • Method: Выпадающий список, где вы выбираете HTTP-метод: `GET`, `POST`, `PUT`, `DELETE` или `PATCH`. Выбор метода определяет намерение вашего запроса.
  • URL: Адрес API-endpoint, к которому вы обращаетесь. Это поле можно оставить пустым, если вы планируете задавать URL динамически через входящее сообщение.
  • Return: Определяет, как Node-RED должен обработать тело ответа от сервера.
  • * a parsed JSON object: Самый частый выбор. Node-RED попытается автоматически преобразовать текстовый ответ (если он в формате JSON) в нативный JavaScript-объект, который сразу же готов к использованию в `msg.payload`.

    * a UTF-8 string: Ответ будет помещен в `msg.payload` как есть, в виде строки. Полезно для получения HTML-страниц или текстовых файлов.

    * a binary buffer: Ответ будет представлен в виде буфера двоичных данных. Необходимо при скачивании файлов, изображений, аудио.

  • Authentication: Позволяет настроить метод аутентификации.
  • * Basic authentication: Использует пару логин/пароль.

    * Bearer authentication: Наиболее распространенный метод для современных API, использующий токен доступа (bearer token).

  • Use TLS: Установите этот флажок для использования защищенного протокола HTTPS. Для интеграции с любыми внешними сервисами использование HTTPS является обязательным стандартом безопасности.
  • Name: Задайте осмысленное имя для ноды, например, "Получить погоду" или "Отправить уведомление в Telegram".
  • Динамическая конфигурация ноды

    Жестко задавать все параметры в настройках ноды — плохая практика, так как это снижает гибкость потока. Нода `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.

  • В настройках ноды `HTTP Request` выберите тип аутентификации, например, "Bearer authentication".
  • Вместо того чтобы вставлять токен прямо в поле, нажмите на иконку карандаша.
  • Откроется окно "Add new credentials". Введите сюда ваш секретный токен и дайте ему имя.
  • Сохраните. Node-RED зашифрует эти данные и будет хранить их отдельно от самого потока. При экспорте потока в JSON-файл вместо вашего токена будет стоять лишь идентификатор этих учетных данных. Это предотвращает случайную утечку секретов.
  • ---

    Практика: GET-запрос для получения данных

    Давайте создадим поток, который будет получать актуальный прогноз погоды для заданного города, используя публичное API. Мы будем использовать сервис, который предоставляет данные в формате JSON.

    > 💡 Подсказка: Используйте онлайн-инструменты для форматирования JSON (JSON formatter), чтобы визуально разобрать структуру ответа API и правильно составить путь для извлечения данных. Это значительно упрощает отладку.

    Пошаговая инструкция

    Задача: Каждые 15 минут запрашивать погоду для Москвы и извлекать из ответа температуру и влажность, приводя их к стандартному контракту сообщения.
  • Создаем поток. Разместите на поле узлы `Inject`, `Change`, `HTTP Request` и `Debug`. Соедините их последовательно.
  • Настраиваем `Inject` (Инициатор).
  • * `Payload`: `timestamp` (по умолчанию).

    * `Repeat`: `interval` every `15` `minutes`.

    * `Name`: "Запрос погоды (15 мин)".

  • Настраиваем `Change` (Формирователь запроса). Этот узел будет динамически формировать URL. Это хорошая практика, позволяющая легко менять город или ключ API в одном месте.
  • * `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` указывает, что мы хотим получить температуру в градусах Цельсия.

  • Настраиваем `HTTP Request` (Исполнитель).
  • * `Method`: `GET` (установлен по умолчанию).

    * `URL`: оставьте это поле пустым, так как мы задаем его динамически в `msg.url`.

    * `Return`: `a parsed JSON object`. Это критически важно, чтобы Node-RED автоматически превратил ответ в удобный для работы объект.

    * `Name`: "GET Weather API".

  • Настраиваем `Debug` (Анализатор).
  • * `Output`: `complete msg object`. Это позволит нам увидеть не только `msg.payload`, но и `msg.statusCode` и `msg.headers` ответа.

  • Запуск и анализ. Нажмите на кнопку узла `Inject`, чтобы выполнить запрос вручную. В панели отладки вы увидите большой объект `msg`. Раскройте `msg.payload`. Вы увидите структуру, подобную этой:
  • {
    

    "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`.

  • Добавьте узел `Change` между `HTTP Request` и `Debug`.
  • Настройте его следующим образом:
  • * `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` (для ручного теста), `Change` (для формирования тела и заголовков запроса) и `HTTP Request`.
  • Настраиваем `Inject`: Создадим два узла `Inject` для удобства.
  • * Первый `Inject`: `Payload` (string) = `ON`, `Topic` (string) = `cmnd/light/on`. `Name`: "Включить".

    * Второй `Inject`: `Payload` (string) = `OFF`, `Topic` (string) = `cmnd/light/off`. `Name`: "Выключить".

  • Настраиваем `Change` (Формирователь POST-запроса): Этот узел будет выполнять всю подготовительную работу.
  • * `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"}`.

  • Настраиваем `HTTP Request`:
  • * `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` несколькими ключевыми свойствами:

    Маршрутизация на основе кода ответа

    Самый надежный способ построить логику обработки — использовать узел `Switch` после ноды `HTTP Request`.

    Пример:
  • После `HTTP Request` поставьте узел `Switch`.
  • `Name`: "Analyze HTTP Status Code".
  • `Property`: `msg.statusCode`.
  • Настройте правила маршрутизации:
  • * Выход 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`. Для отлова всех остальных непредвиденных кодов.

    Стратегии обработки ошибок

    * Логика: Сформировать сообщение об ошибке, записать его в аудиторский журнал (Audit Log).

    * Действие: Отправить уведомление администратору (через MQTT, Telegram и т.д.) с указанием `msg.statusCode` и, возможно, `msg.payload` (где сервер часто описывает причину ошибки).

    * Пример: `node.error("Client Error: " + msg.statusCode, msg);`

    * Логика: Как мы рассматривали ранее, можно использовать узел `trigger`, который после получения сообщения об ошибке ждет некоторое время (например, 1 минуту), а затем снова инициирует запрос.

    * Действие: После нескольких неудачных попыток (например, 3 раза), поток должен прекратить попытки и отправить критическое уведомление. Для подсчета попыток удобно использовать контекст потока (`flow context`).

    ---

    Резюме и лучшие практики

    Интеграция через HTTP API открывает контроллеру HI доступ к безграничному миру внешних данных и сервисов. Правильное применение ноды `HTTP Request` является ключевым навыком для инженера по автоматизации.

    Краткое повторение

    Чек-лист для отладки API-запросов

    Если ваш HTTP-запрос не работает, последовательно проверьте:

  • [ ] URL: Адрес введен без опечаток? Протокол `http://` или `https://` указан верно?
  • [ ] Метод: Выбран правильный метод (`GET`, `POST`) в соответствии с документацией API?
  • [ ] Заголовки: Установлен ли заголовок `Content-Type: application/json` для POST-запросов? Предоставлен ли `Authorization` заголовок, если API требует аутентификации?
  • [ ] Тело запроса (Payload): Ваш JSON валиден? Структура объекта соответствует тому, что ожидает API?
  • [ ] Код ответа: Какой `msg.statusCode` возвращает сервер? `400` — проверь `payload`, `401` — проверь токен, `404` — проверь URL. `500` — проблема на сервере, попробуй позже.
  • [ ] Формат ответа: В ноде `HTTP Request` выбран правильный тип `Return`? Если API отдает JSON, а вы выбрали `a UTF-8 string`, вы получите не объект, а строку.
  • Безопасность и выбор протокола

    > ⚠️ Внимание: Всегда используйте HTTPS вместо HTTP при работе с внешними сервисами через интернет. HTTPS шифрует весь трафик между вашим контроллером и сервером API, защищая передаваемые данные (включая токены и пароли) от перехвата.

    Когда выбирать HTTP API, а когда — MQTT?

    Что дальше

    В этом уроке мы освоили клиент-серверное взаимодействие по протоколу HTTP и научились интегрировать наш контроллер с внешними API. В следующем уроке мы рассмотрим обратную задачу: как превратить сам контроллер HI в HTTP-сервер с помощью нод `HTTP In` и `HTTP Response`, чтобы он мог принимать команды и запросы от других систем в вашей локальной сети.