Отправка POST-запроса из 1С на внешний сервер
Содержание
Чтобы отправить POST-запрос из 1С, нужны три объекта платформы: HTTPСоединение (куда стучимся), HTTPЗапрос (что отправляем) и метод соединения ОтправитьДляОбработки, который и выполняет POST. Код пишется на встроенном языке в любом модуле — в обработке, общем модуле или модуле объекта — и работает на платформе 8.3.
Ниже — рабочий минимум, который можно скопировать и запустить, а затем разбор каждого кусочка: как собрать тело в формате JSON, передать заголовки и токен, прочитать код ответа сервера, настроить HTTPS и что делать, когда запрос не проходит.
Все примеры предназначены для выполнения на сервере (в серверной процедуре или на толстом клиенте). Из тонкого и веб-клиента прямые HTTP-вызовы к произвольным адресам ограничены — переносите их в серверный контекст через &НаСервереБезКонтекста.
Три объекта, из которых собирается запрос
Разберёмся, кто за что отвечает — без этого дальнейший код выглядит набором магии.
- 🔌
HTTPСоединение— транспорт. Хранит адрес сервера, порт, таймаут, параметры прокси и защищённого соединения. Создаётся один раз на сервер. - 📦
HTTPЗапрос— полезная нагрузка. Это относительный адрес ресурса (путь после домена), набор заголовков и тело. - 📨
HTTPОтвет— то, что вернул сервер. Содержит код состояния, заголовки и тело.
Метод, отправляющий данные, выбирается по HTTP-глаголу:
| Нужный метод | Метод HTTPСоединение |
|---|---|
| GET | Получить(Запрос) |
| POST | ОтправитьДляОбработки(Запрос) |
| PUT | Записать(Запрос) |
| DELETE | УдалитьДляОбработки(Запрос) |
| PATCH и прочие | ВызватьHTTPМетод("PATCH", Запрос) |
Если API требует нестандартный глагол, не ищите отдельный метод — используйте универсальный ВызватьHTTPМетод, первым параметром строка-глагол.
Минимальный POST-запрос: пример целиком
Вот законченный фрагмент, который отправляет JSON на https://api.example.com/v1/orders и получает ответ:
// 1. Защищённое соединение по HTTPS, таймаут 30 секунд
Защита = Новый ЗащищенноеСоединениеOpenSSL;
Соединение = Новый HTTPСоединение("api.example.com", 443, , , , 30, Защита);
// 2. Запрос: путь к ресурсу и заголовок с типом содержимого
Запрос = Новый HTTPЗапрос("/v1/orders");
Запрос.Заголовки.Вставить("Content-Type", "application/json");
// 3. Тело запроса строкой в кодировке UTF-8
ТелоСтрока = "{""number"":""0001"",""summ"":1500}";
Запрос.УстановитьТелоИзСтроки(ТелоСтрока, КодировкаТекста.UTF8);
// 4. Отправка POST и чтение ответа
Ответ = Соединение.ОтправитьДляОбработки(Запрос);
Если Ответ.КодСостояния = 200 Тогда
ТекстОтвета = Ответ.ПолучитьТелоКакСтроку();
КонецЕсли;
Разберём подводные камни конструктора HTTPСоединение. Его параметры идут строго по порядку: сервер, порт, пользователь, пароль, прокси, таймаут, защищённое соединение. Поэтому в примере стоят три подряд запятых — мы пропускаем пользователя, пароль и прокси, а 30 попадает именно в таймаут, а Защита — в седьмой параметр.
Адрес сервера указывается без схемы и без пути: только домен api.example.com, а не https://api.example.com/v1/orders. Схему задаёт объект защиты (HTTPS → порт 443 + ЗащищенноеСоединениеOpenSSL), а путь /v1/orders уходит в конструктор HTTPЗапрос.
Если сервер 1С выходит в интернет через прокси, добавьте пятым параметром объект ИнтернетПрокси:
Прокси = Новый ИнтернетПрокси;
Прокси.Установить("https", "proxy.local", 8080);
Соединение = Новый HTTPСоединение("api.example.com", 443, , , Прокси, 30, Защита);
Где писать этот код. Для разовой проверки удобна внешняя обработка с одной кнопкой — её можно открыть в режиме 1С:Предприятие и прогнать запрос в отладчике. В рабочем решении обмен выносят в общий модуль с серверными процедурами и вызывают из регламентного задания по расписанию, чтобы выгрузка не зависела от открытых сеансов пользователей.
Тело запроса: JSON, форма и двоичные данные
Самый частый формат тела — JSON. Писать его строкой с экранированными кавычками, как в примере выше, можно только для совсем простых случаев. Для реальных структур собирайте JSON штатными средствами платформы:
Данные = Новый Структура;
Данные.Вставить("number", "0001");
Данные.Вставить("summ", 1500);
Запись = Новый ЗаписьJSON;
Запись.УстановитьСтроку();
ЗаписатьJSON(Запись, Данные);
ТелоСтрока = Запись.Закрыть();
Запрос.УстановитьТелоИзСтроки(ТелоСтрока, КодировкаТекста.UTF8);
Метод Закрыть() у ЗаписьJSON возвращает готовую строку, потому что перед записью мы вызвали УстановитьСтроку(). Без этого вызова запись ушла бы в файл.
Для формы (как отправляет HTML-страница) тип и формат другие:
Запрос.Заголовки.Вставить("Content-Type", "application/x-www-form-urlencoded");
Запрос.УстановитьТелоИзСтроки("number=0001&summ=1500", КодировкаТекста.UTF8);
Если нужно отправить файл или уже готовый бинарный пакет, используйте УстановитьТелоИзДвоичныхДанных:
Запрос.УстановитьТелоИзДвоичныхДанных(ДвоичныеДанные);
Часть сервисов (особенно государственные и старые корпоративные API) принимает не JSON, а XML. Тело тогда собирается объектом ЗаписьXML, а при обмене типовыми объектами 1С — через СериализаторXDTO:
Запись = Новый ЗаписьXML;
Запись.УстановитьСтроку();
// ... запись узлов через ЗаписатьНачалоЭлемента / ЗаписатьТекст / ЗаписатьКонецЭлемента
ТелоXML = Запись.Закрыть();
Запрос.Заголовки.Вставить("Content-Type", "application/xml");
Запрос.УстановитьТелоИзСтроки(ТелоXML, КодировкаТекста.UTF8);
Главное отличие от JSON — другой заголовок Content-Type (application/xml или text/xml). Формат тела диктует принимающая сторона, и перепутать их — частая причина кода 400.
Кодировка в УстановитьТелоИзСтроки должна совпадать с charset в заголовке Content-Type. Если шлёте UTF-8, а сервер ждёт windows-1251 (или наоборот), в полях с кириллицей на приёмной стороне будут кракозябры. При сомнениях ставьте UTF-8 и явно пишите application/json; charset=utf-8.
Заголовки и авторизация
Заголовки — это свойство Заголовки объекта HTTPЗапрос, тип — Соответствие. Добавляются через Вставить("Имя", "Значение").
Два типовых способа авторизации.
Basic (логин и пароль). Пара логин:пароль кодируется в Base64:
ЛогинПароль = "user:secret";
Двоичные = ПолучитьДвоичныеДанныеИзСтроки(ЛогинПароль, КодировкаТекста.UTF8);
КлючBase64 = СтрЗаменить(Base64Строка(Двоичные), Символы.ПС, "");
Запрос.Заголовки.Вставить("Authorization", "Basic " + КлючBase64);
Здесь важен СтрЗаменить(..., Символы.ПС, ""): Base64Строка для длинных строк вставляет переводы строки, а в значении заголовка их быть не должно — иначе сервер вернёт ошибку.
Bearer (токен). Проще: подставляем токен в готовом виде.
Запрос.Заголовки.Вставить("Authorization", "Bearer " + Токен);
Логин и пароль для Basic можно задать и прямо в конструкторе HTTPСоединение (третий и четвёртый параметры) — тогда платформа сформирует заголовок сама. Но ручной заголовок нагляднее и работает со всеми схемами, поэтому для API он предпочтительнее.
Не держите токены и пароли в тексте модуля. Храните их в константе, параметре сеанса или в безопасном хранилище данных и подставляйте в заголовок уже в момент запроса.
Что вернул сервер: код состояния и разбор ответа
После ОтправитьДляОбработки работаем с объектом HTTPОтвет. Ключевое свойство — КодСостояния (целое число). Проверять его нужно всегда: отсутствие исключения не означает, что сервер принял данные.
| Код | Что означает |
|---|---|
| 200, 201, 204 | Успех (создано, принято, без содержимого) |
| 400 | Сервер не разобрал тело — проверьте JSON |
| 401 | Нет авторизации или просрочен токен |
| 403 | Авторизация есть, но доступ запрещён |
| 404 | Неверный путь ресурса |
| 415 | Не указан или неверный Content-Type |
| 429 | Слишком много запросов — нужна пауза |
| 500, 502, 503 | Ошибка на стороне сервера |
Тело ответа читается строкой, а дальше разбирается из JSON обратно в структуру данных:
Ответ = Соединение.ОтправитьДляОбработки(Запрос);
Если Ответ.КодСостояния = 200 Или Ответ.КодСостояния = 201 Тогда
ТекстОтвета = Ответ.ПолучитьТелоКакСтроку();
Чтение = Новый ЧтениеJSON;
Чтение.УстановитьСтроку(ТекстОтвета);
Результат = ПрочитатьJSON(Чтение, Истина); // Истина → читать в Соответствие
Чтение.Закрыть();
Иначе
// код состояния и тело с описанием ошибки — в журнал регистрации
ЗаписьЖурналаРегистрации("Обмен.POST", УровеньЖурналаРегистрации.Ошибка,
, , "Код " + Ответ.КодСостояния + ": " + Ответ.ПолучитьТелоКакСтроку());
КонецЕсли;
Второй параметр ПрочитатьJSON(Чтение, Истина) просит вернуть Соответствие, а не Структуру. Это безопаснее: ключи JSON бывают с дефисами, цифрами в начале и прочими символами, которые не годятся в качестве имён свойств структуры, и чтение в структуру на таких ключах упадёт. Если верхний уровень JSON — массив (ответ начинается с [), ПрочитатьJSON вернёт Массив, по которому идут обычным циклом.
Когда сервер отвечает в XML, тело разбирается объектом ЧтениеXML (пошаговый обход узлов), а типовые пакеты обмена — через СериализаторXDTO.ПрочитатьXML. Принцип тот же: сначала строка из ПолучитьТелоКакСтроку, затем разбор подходящим объектом.
Заголовки ответа тоже доступны — через Ответ.Заголовки, это Соответствие. Оттуда берут, например, Content-Type ответа или заголовки пагинации.
Тело ответа читается целиком в память. Для больших файлов вместо ПолучитьТелоКакСтроку берите ПолучитьТелоКакФайл или ПолучитьТелоКакДвоичныеДанные и сохраняйте на диск, не держа гигабайты в переменной.
Повторные попытки и устойчивость обмена
Сеть иногда подводит: сервер отвечает кодом 429 (слишком много запросов), 503 (временно недоступен) или вовсе обрывает соединение по таймауту. Для таких случаев обмен оборачивают в попытку с несколькими повторами и паузой между ними:
Попытка = 1;
Пока Попытка <= 3 Цикл
Попытка
Ответ = Соединение.ОтправитьДляОбработки(Запрос);
Если Ответ.КодСостояния < 500 И Ответ.КодСостояния <> 429 Тогда
Прервать; // ответ получен, повтор не нужен
КонецЕсли;
Исключение
// сетевая ошибка — попробуем ещё раз
КонецПопытки;
Попытка = Попытка + 1;
// небольшая пауза перед следующей попыткой
КонецЦикла;
Повторять вслепую можно только безопасные операции. Если POST создаёт документ или списывает деньги, повтор после неясного ответа способен задвоить операцию. Для таких запросов используйте ключ идемпотентности: передавайте в заголовке уникальный идентификатор операции (его поддерживают почти все платёжные и учётные API), тогда сервер распознает повтор и не выполнит действие дважды.
Не ставьте таймаут в ноль «чтобы ждать сколько угодно»: зависший сервер тогда остановит ваше регламентное задание навсегда. Разумная пауза — 30–60 секунд, а долгие операции выносите в асинхронную схему, где сервер отдаёт идентификатор задачи, а вы опрашиваете его статус отдельными запросами.
HTTPS и работа с сертификатами
Для адреса на https:// обязательны две вещи: порт 443 и объект защищённого соединения седьмым параметром конструктора.
В большинстве случаев хватает пустого конструктора — платформа возьмёт доверенные корневые сертификаты из системного хранилища:
Защита = Новый ЗащищенноеСоединениеOpenSSL;
Проблемы начинаются, когда сервер 1С работает на Linux, а у внешнего API сертификат, выпущенный центром, которого нет в хранилище OpenSSL. Тогда запрос падает с ошибкой проверки сертификата. Варианты решения:
- Добавить корневой сертификат удостоверяющего центра в хранилище OpenSSL операционной системы, где крутится сервер 1С.
- Передать корневые сертификаты явно, вторым параметром конструктора
ЗащищенноеСоединениеOpenSSL. - Если API требует взаимной аутентификации (клиентский сертификат) — передать первым параметром объект
СертификатКлиентаФайлс путём к сертификату и паролем.
Отключать проверку сертификата целиком, лишь бы «заработало», нельзя: это открывает соединение для подмены. Если тестовый стенд отдаёт самоподписанный сертификат, добавьте именно его в доверенные, а не снимайте проверку.
Отладка и частые ошибки
Перед тем как винить платформу, проверьте запрос независимым инструментом — например, тем же телом и заголовками через консольную утилиту или Postman. Если там проходит, а в 1С нет, дело в коде на встроенном языке, а не в сервере.
Что помогает при отладке:
- 🔎 Логируйте в журнал регистрации и код состояния, и тело ответа при ошибке — сервер почти всегда возвращает текст с причиной.
- 🧪 Соберите внешнюю обработку
с одной кнопкой и прогоняйте запрос в отладчике, глядя на свойства
Ответв табло. - 📝 Временно выведите готовое тело запроса (
ТелоСтрока) в сообщение — убедитесь, что JSON валиден и кавычки не «съелись».
Типовые симптомы и причины:
ЗащищенноеСоединениеOpenSSL или у сервера 1С нет корневого сертификата удостоверяющего центра. Передайте объект защиты седьмым параметром, на Linux добавьте CA в хранилище OpenSSL.Authorization. Проверьте токен и то, что в Base64 для Basic не попал перевод строки.ТелоСтрока и проверьте структуру.Content-Type либо он не тот, что ждёт API. Добавьте application/json.HTTPСоединение (в секундах).УстановитьТелоИзСтроки не совпадает с charset в Content-Type. Приведите обе к UTF-8.Частые вопросы
Чем POST отличается от GET в 1С технически?
Получить и не несёт тела — параметры идут в адресе ресурса. POST вызывается методом ОтправитьДляОбработки, и данные передаются в теле запроса через УстановитьТелоИзСтроки или УстановитьТелоИзДвоичныхДанных.Как отправить PUT, PATCH или DELETE, если отдельного метода нет?
Записать (PUT) и УдалитьДляОбработки (DELETE) либо универсальным ВызватьHTTPМетод("PATCH", Запрос) — первым параметром строка с нужным глаголом. Тело и заголовки собираются так же, как для POST.Можно ли переиспользовать одно `HTTPСоединение` для нескольких запросов?
HTTPЗапрос — под каждый вызов. Это экономит время на повторную установку TLS-сессии при серии обращений к одному API.Почему тело приходит на сервер пустым?
HTTPЗапрос, а отправляется через HTTPСоединение. Проверьте, что УстановитьТелоИзСтроки вызван до ОтправитьДляОбработки.Как увидеть сырой ответ сервера для разбора?
Ответ.ПолучитьТелоКакСтроку() и выведите результат в сообщение или журнал регистрации. Код состояния смотрите в Ответ.КодСостояния, заголовки — в Ответ.Заголовки.