Заказчик дал 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.