Заказчик дал Swagger внешнего сервиса, а в поиске по "битрикс интеграция api" открывается документация Bitrix24 CRM - не то, что нужно для 1С-Битрикс: Управление сайтом. На практике интеграция не работает: вы тянете curl в агент, получаете getStatus() === 0 без ошибки, токен уезжает в лог. Ниже - как из модуля отправить исходящий GET/POST через HttpClient D7: JSON, Bearer из настроек, таймауты и отладка без утечки секретов.
Исходящий HTTP из PHP-модуля CMS делается классом \Bitrix\Main\Web\HttpClient, а не входящим /rest/ на вашем домене. Минимальный каркас: new HttpClient с таймаутами, setHeader для JSON и Authorization, Json::encode в post, проверка getStatus и getError. Токен храните в Option, глобальные лимиты - в http_client_options в .settings.php.
API (Application Programming Interface) - это "язык", на котором ваш сайт договаривается с чужим сервисом: вы шлете запрос, получаете ответ. На CMS задача обычно звучит так: агент или обработчик события должен дернуть api.example.com и записать результат в инфоблок. Для этого в ядре есть встроенный клиент - без file_get_contents и без ручного curl_init, если следовать правилам ниже.
Разберите, когда нужен HttpClient на CMS, а не входящий REST
Три сценария часто путают. Первый - облачный CRM-портал: внешний сервис стучится в чужой облачный REST. Второй - входящий REST на вашем сайте: партнер вызывает https://ваш-сайт.ru/rest/... Третий - исходящий вызов: PHP-код на CMS сам идет наружу, например к платежному или логистическому API. Для третьего как раз HttpClient.
Если форма на странице шлет AJAX без перезагрузки - смотрите BX.ajax.runAction и Engine\Controller. Если нужен каркас модуля, куда положить клиент - создание своего модуля. HttpClient живет внутри модуля или агента и не заменяет ни REST-вебхук на сайте, ни фронтовый AJAX.
| Задача | Инструмент | Направление |
|---|---|---|
| Внешний сервис дергает ваш сайт | REST-модуль, /rest/ | Входящий |
| Сайт дергает api.example.com | HttpClient | Исходящий |
| Кнопка на странице без reload | BX.ajax.runAction | Внутри браузера |
Делайте: перед кодом сформулируйте направление запроса. Не делайте: копировать CRM-методы crm.lead.add из облачных гайдов - на Управлении сайтом они не заработают.
Соберите минимальный GET и POST через legacy HttpClient
Класс \Bitrix\Main\Web\HttpClient входит в главный модуль. По умолчанию он ходит в сеть через сокеты; опцию useCurl включают, если хостинг режет сокеты.
- Подключите модуль и создайте клиент с таймаутами, чтобы агент не зависал.
- Для GET вызовите get($url) и сразу проверьте getStatus().
- Для POST передайте тело вторым аргументом в post().
- Разберите ответ через getResult(); при сбое смотрите getError().
- Залогируйте код и укороченное тело, без заголовков Authorization.
- При getStatus() === 0 откройте getError() и проверьте DNS, SSL и таймауты.
use Bitrix\Main\Web\HttpClient;
use Bitrix\Main\Web\Json;
$http = new HttpClient([
'socketTimeout' => 10,
'streamTimeout' => 20,
]);
$http->get('https://api.example.com/v1/status');
if ($http->getStatus() !== 200) {
// transport или HTTP-ошибка
$errors = $http->getError();
}
Делайте: заменяйте curl в агенте на HttpClient - ядро уже учитывает окружение Битрикс. Не делайте: игнорировать getStatus() === 0: это не "пустой ответ API", а сбой транспорта (DNS, SSL, таймаут).
Настройте JSON POST и Bearer-токен из Option
Внешние API почти всегда ждут JSON и заголовок Content-Type: application/json. Токен доступа не хардкодьте в файле - положите в настройки модуля через Option API.
use Bitrix\Main\Config\Option;
use Bitrix\Main\Web\HttpClient;
use Bitrix\Main\Web\Json;
$token = Option::get('vendor.api', 'token', '');
$http = new HttpClient(['socketTimeout' => 15, 'streamTimeout' => 30]);
$http->setHeader('Content-Type', 'application/json');
$http->setHeader('Authorization', 'Bearer ' . $token);
$payload = ['order_id' => 1001, 'status' => 'paid'];
$http->post('https://api.example.com/v1/orders', Json::encode($payload));
$status = $http->getStatus();
$body = Json::decode($http->getResult());
Типичная ошибка: отправить json_encode без Content-Type - сервер отвечает 400, а в логе "непонятный мусор". Вторая ошибка: var_dump($http) на проде и утечка Bearer в лог.
Делайте: Json::encode из ядра и отдельный setHeader для Authorization. Не делайте: писать токен в git и в AddMessage2Log вместе с заголовками.
Задайте таймауты, прокси и http_client_options в .settings.php
Дефолты ядра: socketTimeout 30 секунд, streamTimeout 60 секунд. Для тяжелых агентов задайте меньше в конструкторе или глобально в /bitrix/.settings.php (с main 16.0.14):
'http_client_options' => [
'socketTimeout' => 10,
'streamTimeout' => 25,
'redirect' => true,
'redirectMax' => 5,
],
Проверка значений в коде: \Bitrix\Main\Config\Configuration::getValue('http_client_options'). Прокси задают ключом proxy в том же массиве, если исходящий трафик идет через корпоративный шлюз.
Схема настройки:
Агент/событие → new HttpClient(локальные опции) → merge с http_client_options → запрос к API → getStatus/getResult
Делайте: ограничивайте таймауты на проде, чтобы PHP-FPM не копил зависшие воркеры. Не делайте: полагаться на бесконечное ожидание "раз с curl работало".
Отправьте multipart и файлы через CFile::MakeFileArray
Если API принимает файл, третий параметр post(..., true) включает multipart/form-data (с main 17.5.5). Массив файла готовят как для $_FILES - через CFile::MakeFileArray:
$send = [
'comment' => 'Акт сверки',
'file' => \CFile::MakeFileArray('/upload/tmp/act.pdf'),
];
$http->post('https://api.example.com/v1/upload', $send, true);
Для нестандартных контрактов с main 23.0.0 доступен PSR-18 режим: Request, Stream, sendRequest. FormStream упрощает тела форм с main 23.300.0. Если legacy post хватает - не усложняйте.
Делайте: закрывайте fopen-ресурсы после отправки. Не делайте: собирать boundary вручную, пока штатный multipart не отвергнут.
Перейдите на PSR-18, когда legacy get/post не хватает
PSR-18 - общий стандарт PHP для HTTP-клиентов. В Битрикс sendRequest появился с main 23.0.0. Важное отличие от legacy: редиректы 30x не обрабатываются автоматически - Location нужно читать сами. В legacy get/post редиректы включены (redirect true, redirectMax 5).
Когда переходить: нужны точные заголовки, нестандартное тело, интеграция с библиотекой под PSR-18. Когда оставить legacy: простой GET/POST JSON в агенте.
Делайте: проверяйте версию main перед PSR-18. Не делайте: ждать автоматического редиректа в sendRequest - получите "лишний" 302 в getStatus().
Пройдите чек-лист отладки и безопасности
После тестового вызова к https://api.example.com/... вы должны получить getStatus() === 200 и разобранный Json::decode. Если status 0 - смотрите getError(), DNS, SSL и таймауты. HTTP 401/403 - это уже ответ сервера, не транспорт.
- Токен в Option, не в репозитории.
- Лог: код ответа + первые 200 символов тела, без Authorization.
- URL от пользователя: privateIp => false блокирует запросы к 127.0.0.1 и внутренним сетям (защита от SSRF).
- disableSslVerification - только на локальном стенде, не на проде.
Если интеграция упирается в архитектуру модуля и внешних API, можно обсудить проект - разберем сценарий до рабочего агента на стенде.
Что дальше
- Вынесите URL и таймауты в Option и http_client_options.
- Прогоните тестовый POST на staging с логом без секретов.
- Сверьте направление: исходящий HttpClient, не входящий REST.
- Добавьте обработку 4xx/5xx в бизнес-логику модуля.
- Посмотрите кейсы по интеграциям для похожих задач.
Автор: Максим Мольков, разработчик 1С-Битрикс.
Источники: HTTP-клиент (docs.1c-bitrix.ru), справочник HttpClient D7, PSR-18 в main.
Частые вопросы
Чем HttpClient в Битрикс отличается от curl?
HttpClient встроен в ядро, подхватывает http_client_options из .settings.php, дает getStatus и getError для диагностики. curl в агенте - "велосипед" без единых таймаутов и без связки с Option. Для исходящих вызовов с CMS берите HttpClient.
Как отправить POST с JSON во внешний API?
Создайте HttpClient, setHeader('Content-Type', 'application/json'), передайте Json::encode($data) вторым аргументом в post(). Проверьте getStatus() и разберите тело через Json::decode.
Где настроить таймаут HttpClient?
В конструкторе new HttpClient(['socketTimeout' => N, 'streamTimeout' => M]) или глобально в секции http_client_options файла /bitrix/.settings.php. Для агентов ставьте жесткие лимиты.
Чем исходящий API-запрос отличается от REST API на сайте?
Исходящий: ваш PHP-код идет к чужому домену через HttpClient. Входящий REST: внешний клиент вызывает /rest/ на вашем сайте. Это разные задачи и разные настройки.
Как прикрепить файл к POST-запросу?
Соберите массив с CFile::MakeFileArray и вызовите post($url, $array, true) - третий параметр true включает multipart. Для сложных API используйте PSR-18 MultipartStream с main 23.0.0+.
Что значит getStatus() === 0?
Запрос не дошел до HTTP-ответа: сеть, DNS, SSL или таймаут. Откройте getError() и логи сервера. Это не то же самое, что код 404 или 500 от API.
Было полезно?
Поставьте лайк, если статья помогла разобраться.
Поделиться статьёй
Отправьте тому, кому это пригодится.
Можно поделиться и фрагментом: выделите текст статьи.