HTTP-запросы из 1С к REST API: GET, POST и JSON
Интеграция 1С с внешними сервисами почти всегда сводится к HTTP-запросам: получить курс, отправить заказ, передать документ в маркетплейс. В статье — рабочий код на встроенном языке для GET и POST запросов по HTTPS, авторизация Bearer и Basic, передача JSON, работа через прокси, отправка файла и функция-обёртка, которую можно сразу взять в общий модуль.
Задача: обратиться из 1С к внешнему API
Почти любой современный сервис отдаёт данные через REST API: запрос по HTTPS, параметры в адресе или JSON в теле, ответ в JSON. Для 1С API интеграция строится на трёх объектах: HTTPСоединение описывает сервер, HTTPЗапрос — ресурс, заголовки и тело, HTTPОтвет — то, что вернул сервер. Ниже по шагам: простой GET, POST с JSON, авторизация, прокси, файлы и общая функция для всего этого.
GET запрос по HTTPS
Минимальный 1С get запрос: создаём защищённое соединение, отправляем запрос, проверяем код состояния и читаем тело как строку.
&НаСервереБезКонтекста
Функция ПолучитьКурсы()
Сервер = "api.example.com";
Ресурс = "/v1/rates?base=" + КодироватьСтроку("RUB", СпособКодированияСтроки.КодировкаURL);
Соединение = Новый HTTPСоединение(Сервер, 443, , , , 30, Новый ЗащищенноеСоединениеOpenSSL);
Заголовки = Новый Соответствие;
Заголовки.Вставить("Accept", "application/json");
Запрос = Новый HTTPЗапрос(Ресурс, Заголовки);
Ответ = Соединение.Получить(Запрос);
Если Ответ.КодСостояния <> 200 Тогда
ВызватьИсключение СтрШаблон("Сервис вернул код %1: %2",
Ответ.КодСостояния, Ответ.ПолучитьТелоКакСтроку());
КонецЕсли;
Чтение = Новый ЧтениеJSON;
Чтение.УстановитьСтроку(Ответ.ПолучитьТелоКакСтроку(КодировкаТекста.UTF8));
Данные = ПрочитатьJSON(Чтение, Истина);
Чтение.Закрыть();
Возврат Данные;
КонецФункции
Обратите внимание: в конструктор HTTPСоединение передаётся только имя хоста, без https:// и без пути. Путь и параметры идут в первый аргумент HTTPЗапрос. Шестой параметр конструктора — таймаут в секундах; без него при зависании сервиса сеанс будет ждать очень долго. Седьмой — объект ЗащищенноеСоединениеOpenSSL, он включает HTTPS. Для обычного HTTP порт 80 и седьмой параметр не указывается.
POST запрос с JSON-телом
Для 1С post запрос отличается тем, что у него есть тело. Структуру сериализуем в строку через ЗаписьJSON и кладём в запрос методом УстановитьТелоИзСтроки. Важная деталь — отключить BOM: многие серверы не ожидают служебных байтов в начале JSON и отвечают ошибкой разбора.
&НаСервереБезКонтекста
Функция СоздатьЗаказ(Токен)
Заказ = Новый Структура;
Заказ.Вставить("number", "A-1024");
Заказ.Вставить("amount", 1500.50);
Заказ.Вставить("items", Новый Массив);
Заказ.items.Добавить(Новый Структура("sku, qty", "SKU-1", 2));
Запись = Новый ЗаписьJSON;
Запись.УстановитьСтроку();
ЗаписатьJSON(Запись, Заказ);
ТекстТела = Запись.Закрыть();
Заголовки = Новый Соответствие;
Заголовки.Вставить("Content-Type", "application/json; charset=utf-8");
Заголовки.Вставить("Accept", "application/json");
Заголовки.Вставить("Authorization", "Bearer " + Токен);
Запрос = Новый HTTPЗапрос("/v1/orders", Заголовки);
Запрос.УстановитьТелоИзСтроки(ТекстТела, КодировкаТекста.UTF8, ИспользованиеByteOrderMark.НеИспользовать);
Соединение = Новый HTTPСоединение("api.example.com", 443, , , , 60, Новый ЗащищенноеСоединениеOpenSSL);
Ответ = Соединение.ОтправитьДляОбработки(Запрос);
Возврат Ответ;
КонецФункции
Для методов PUT, PATCH и DELETE используйте универсальный Соединение.ВызватьHTTPМетод("PATCH", Запрос): первым параметром передаётся имя метода строкой.
Авторизация: Bearer и Basic
С токеном всё просто — заголовок Authorization: Bearer <токен>, как в примере выше. Для Basic-авторизации есть два способа. Первый — передать логин и пароль третьим и четвёртым параметрами конструктора HTTPСоединение. Второй — сформировать заголовок вручную; это надёжнее, если сервер не присылает запрос авторизации и ждёт заголовок сразу:
Функция ЗаголовокBasic(Логин, Пароль) ДД = ПолучитьДвоичныеДанныеИзСтроки(Логин + ":" + Пароль, КодировкаТекста.UTF8, Ложь); Строка64 = Base64Строка(ДД); // Base64Строка разбивает длинный результат на строки — убираем переносы Строка64 = СтрЗаменить(Строка64, Символы.ВК, ""); Строка64 = СтрЗаменить(Строка64, Символы.ПС, ""); Возврат "Basic " + Строка64; КонецФункции
Токены и пароли не храните в коде модуля: используйте константы с ограниченным доступом или безопасное хранилище БСП, если оно есть в конфигурации.
Работа через прокси
Если сервер 1С выходит в интернет через прокси, передайте объект ИнтернетПрокси пятым параметром соединения. Конструктор с параметром Истина берёт системные настройки, с Ложь — создаёт пустой объект, который заполняется вручную.
Прокси = Новый ИнтернетПрокси(Ложь);
Прокси.Установить("https", "proxy.local", 3128, "proxyuser", "proxypass", Ложь);
Прокси.НеИспользоватьПроксиДляЛокальныхАдресов = Истина;
Соединение = Новый HTTPСоединение("api.example.com", 443, , , Прокси, 30, Новый ЗащищенноеСоединениеOpenSSL);
Отправка файла
Если API принимает файл «как есть» в теле запроса, достаточно метода УстановитьИмяФайлаТела или УстановитьТелоИзДвоичныхДанных. Для формы multipart/form-data тело собирается вручную из частей:
Функция ОтправитьФайл(Соединение, ПутьКФайлу, ИмяФайла)
Граница = СтрЗаменить(Строка(Новый УникальныйИдентификатор), "-", "");
ПС = Символы.ВК + Символы.ПС;
Начало = "--" + Граница + ПС
+ "Content-Disposition: form-data; name=""file""; filename=""" + ИмяФайла + """" + ПС
+ "Content-Type: application/octet-stream" + ПС + ПС;
Конец = ПС + "--" + Граница + "--" + ПС;
Части = Новый Массив;
Части.Добавить(ПолучитьДвоичныеДанныеИзСтроки(Начало, КодировкаТекста.UTF8, Ложь));
Части.Добавить(Новый ДвоичныеДанные(ПутьКФайлу));
Части.Добавить(ПолучитьДвоичныеДанныеИзСтроки(Конец, КодировкаТекста.UTF8, Ложь));
Заголовки = Новый Соответствие;
Заголовки.Вставить("Content-Type", "multipart/form-data; boundary=" + Граница);
Запрос = Новый HTTPЗапрос("/v1/upload", Заголовки);
Запрос.УстановитьТелоИзДвоичныхДанных(СоединитьДвоичныеДанные(Части));
Возврат Соединение.ОтправитьДляОбработки(Запрос);
КонецФункции
Универсальная функция-обёртка с логированием
В реальной интеграции удобно иметь одну функцию в серверном общем модуле, которая выполняет запрос, ловит сетевые исключения, разбирает JSON и пишет ошибки в журнал регистрации. Тогда весь код 1С подключение по API сводится к одной строке вызова.
// Возвращает структуру: Успех, КодСостояния, Данные, ТекстОшибки
Функция ВыполнитьЗапросAPI(Метод, Ресурс, Тело = Неопределено, Токен = "") Экспорт
Результат = Новый Структура("Успех, КодСостояния, Данные, ТекстОшибки", Ложь, 0, Неопределено, "");
Заголовки = Новый Соответствие;
Заголовки.Вставить("Accept", "application/json");
Если ЗначениеЗаполнено(Токен) Тогда
Заголовки.Вставить("Authorization", "Bearer " + Токен);
КонецЕсли;
Запрос = Новый HTTPЗапрос(Ресурс, Заголовки);
Если Тело <> Неопределено Тогда
Запись = Новый ЗаписьJSON;
Запись.УстановитьСтроку();
ЗаписатьJSON(Запись, Тело);
Запрос.Заголовки.Вставить("Content-Type", "application/json; charset=utf-8");
Запрос.УстановитьТелоИзСтроки(Запись.Закрыть(), КодировкаТекста.UTF8,
ИспользованиеByteOrderMark.НеИспользовать);
КонецЕсли;
Попытка
Соединение = Новый HTTPСоединение("api.example.com", 443, , , , 30, Новый ЗащищенноеСоединениеOpenSSL);
Ответ = Соединение.ВызватьHTTPМетод(ВРег(Метод), Запрос);
Исключение
Результат.ТекстОшибки = ПодробноеПредставлениеОшибки(ИнформацияОбОшибке());
ЗаписьЖурналаРегистрации("Интеграция.API", УровеньЖурналаРегистрации.Ошибка, , ,
Метод + " " + Ресурс + Символы.ПС + Результат.ТекстОшибки);
Возврат Результат;
КонецПопытки;
Результат.КодСостояния = Ответ.КодСостояния;
ТекстОтвета = Ответ.ПолучитьТелоКакСтроку(КодировкаТекста.UTF8);
Результат.Успех = (Ответ.КодСостояния >= 200 И Ответ.КодСостояния < 300);
Если Результат.Успех И ЗначениеЗаполнено(ТекстОтвета) Тогда
Попытка
Чтение = Новый ЧтениеJSON;
Чтение.УстановитьСтроку(ТекстОтвета);
Результат.Данные = ПрочитатьJSON(Чтение, Истина);
Чтение.Закрыть();
Исключение
Результат.Данные = ТекстОтвета;
КонецПопытки;
ИначеЕсли Не Результат.Успех Тогда
Результат.ТекстОшибки = ТекстОтвета;
ЗаписьЖурналаРегистрации("Интеграция.API", УровеньЖурналаРегистрации.Предупреждение, , ,
СтрШаблон("%1 %2: код %3%4%5", Метод, Ресурс, Ответ.КодСостояния, Символы.ПС, Лев(ТекстОтвета, 2000)));
КонецЕсли;
Возврат Результат;
КонецФункции
Вызов выглядит так: Рез = ИнтеграцияAPI.ВыполнитьЗапросAPI("POST", "/v1/orders", Заказ, Токен). Адрес сервера в реальном модуле лучше вынести в параметр или настройку. Тело ответа обрезается до 2000 символов, чтобы журнал регистрации не разрастался.
Как разбирать коды ответа
| Код | Что означает | Что делать |
|---|---|---|
| 200, 201, 204 | Успех | Читать тело (у 204 его нет) |
| 400 | Неверный запрос | Проверить JSON и обязательные поля, смотреть текст ответа |
| 401, 403 | Нет авторизации или прав | Проверить токен, срок его действия, заголовок Authorization |
| 404 | Ресурс не найден | Проверить путь и версию API |
| 415 | Неподдерживаемый тип | Указать Content-Type: application/json |
| 429 | Слишком много запросов | Сделать паузу и повторить, соблюдать лимиты сервиса |
| 500–504 | Ошибка сервера | Повторить позже, залогировать запрос |
Повторные попытки и лимиты сервиса
Внешний сервис может временно не отвечать или ограничивать частоту обращений. Если интеграция работает в регламентном задании, разумно повторять запрос при сетевой ошибке и кодах 429, 502, 503, 504, но не бесконечно, а два-три раза с паузой. Запросы с кодами 400, 401 и 404 повторять бессмысленно — ошибка не исчезнет сама. Многие сервисы в ответе 429 присылают заголовок Retry-After с числом секунд, которое нужно подождать; его можно прочитать из Ответ.Заголовки. Обратите внимание на регистр имён: сервер может прислать заголовок как Retry-After или retry-after, поэтому при поиске в соответствии лучше перебрать ключи и сравнить их через НРег.
// Решает, имеет ли смысл повторить запрос позже
Функция НужноПовторить(Рез) Экспорт
Если Рез.Успех Тогда
Возврат Ложь;
КонецЕсли;
// 0 — ответ не получен (сетевая ошибка или таймаут)
Возврат Рез.КодСостояния = 0
Или Рез.КодСостояния = 429
Или Рез.КодСостояния = 502
Или Рез.КодСостояния = 503
Или Рез.КодСостояния = 504;
КонецФункции
// В регламентном задании
Рез = ИнтеграцияAPI.ВыполнитьЗапросAPI("POST", "/v1/orders", Заказ, Токен);
Если ИнтеграцияAPI.НужноПовторить(Рез) Тогда
// оставить заказ в очереди отправки и увеличить счётчик попыток;
// следующий запуск регламентного задания отправит его снова
ИначеЕсли Не Рез.Успех Тогда
// ошибка данных: пометить заказ как ошибочный и показать текст пользователю
КонецЕсли;
Во встроенном языке нет оператора «заснуть», поэтому паузу между повторами проще всего получить за счёт расписания: неотправленные объекты остаются в очереди (например, в регистре сведений со счётчиком попыток), а регламентное задание раз в несколько минут обрабатывает очередь заново. После трёх-пяти неудачных попыток объект стоит пометить как проблемный, чтобы он не отправлялся бесконечно.
Где хранить настройки и как отлаживать
Адрес сервера, порт, токен и таймаут лучше хранить не в коде, а в константах или отдельном справочнике настроек интеграции. Тогда тестовый и рабочий контуры переключаются без изменения конфигурации, а токен можно обновить, не привлекая программиста. Для отладки удобно сначала повторить запрос в Postman или через curl: если там всё работает, а из 1С нет, сравните заголовки и тело байт в байт. Частые расхождения — лишний BOM, другой Content-Type, неэкранированные символы в параметрах. На время отладки можно сохранять тело запроса и ответа в файл или в журнал регистрации, но в рабочей базе не пишите в журнал токены и персональные данные.
Если сервис работает только по TLS 1.2 и выше, а соединение не устанавливается, проверьте версию платформы и операционной системы на сервере 1С: в старых релизах и старых ОС поддержка современных протоколов может отсутствовать. Ошибки проверки сертификата решаются передачей в ЗащищенноеСоединениеOpenSSL хранилища корневых сертификатов операционной системы или файла с нужным сертификатом.
Типичные ошибки
- Передача в
HTTPСоединениеполного URL сhttps://и путём — соединение не установится. Нужен только хост. - Отсутствие
ЗащищенноеСоединениеOpenSSLпри обращении на порт 443: сервер ответит ошибкой или закроет соединение. - BOM в теле запроса из-за
УстановитьТелоИзСтрокибез третьего параметра. - Нет таймаута — при недоступности сервиса фоновое задание висит часами.
- Кириллица и пробелы в параметрах GET без
КодироватьСтроку. - Ожидание, что исключение возникнет при коде 4xx или 5xx. Платформа исключение не вызывает: код нужно проверять самостоятельно, исключение будет только при сетевой ошибке.
- Если сервер отвечает 301 или 302, посмотрите заголовок
LocationвОтвет.Заголовки— обычно это переход с HTTP на HTTPS или на другой адрес, и запрос нужно отправить по новому адресу.
REST в 1С в обратную сторону
Всё сказанное касается вызова чужих API из 1С. Если нужно, чтобы внешние системы обращались к самой базе, rest в 1С реализуется через HTTP-сервисы в конфигурации (свои URL-шаблоны и обработчики методов) либо через стандартный интерфейс OData, который отдаёт объекты конфигурации без программирования. В обоих случаях базу публикуют на веб-сервере Apache или IIS, а клиенты работают с ней как с обычным 1С REST API.
Актуальность для новых версий
Код рассчитан на платформу 8.3 актуальных релизов (8.3.1x–8.3.2x). Метод ВызватьHTTPМетод и функция СоединитьДвоичныеДанные доступны в 8.3 начиная с промежуточных релизов, в старых версиях вместо первого используйте Получить или ОтправитьДляОбработки. Для проверки сертификатов сервера по хранилищу Windows можно передать в ЗащищенноеСоединениеOpenSSL объект СертификатыУдостоверяющихЦентровОС. HTTP-запросы выполняйте на сервере: в веб-клиенте объект HTTPСоединение недоступен.
Частые вопросы
Где выполнять HTTP-запрос — на клиенте или на сервере?
Лучше на сервере: там доступен HTTPСоединение во всех вариантах клиента, а ключи и токены не попадают на компьютер пользователя. С клиента вызывайте серверную процедуру общего модуля.
Почему POST запрос из 1С возвращает 415 или 400?
Чаще всего не указан заголовок Content-Type: application/json или в тело попал BOM. Устанавливайте тело методом УстановитьТелоИзСтроки с параметром ИспользованиеByteOrderMark.НеИспользовать.
Как передать параметры в GET запросе?
Добавьте их в строку ресурса после знака вопроса, а значения закодируйте функцией КодироватьСтроку с СпособКодированияСтроки.КодировкаURL, иначе пробелы и кириллица сломают адрес.
Может ли 1С сама предоставлять REST API?
Да. Для этого в конфигурации создаются HTTP-сервисы или включается стандартный интерфейс OData, после чего база публикуется на веб-сервере.