Отправка POST-запроса из 1С на внешний сервер

· 9 мин

Содержание

Чтобы отправить 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. Тогда запрос падает с ошибкой проверки сертификата. Варианты решения:

  1. Добавить корневой сертификат удостоверяющего центра в хранилище OpenSSL операционной системы, где крутится сервер 1С.
  2. Передать корневые сертификаты явно, вторым параметром конструктора ЗащищенноеСоединениеOpenSSL.
  3. Если API требует взаимной аутентификации (клиентский сертификат) — передать первым параметром объект СертификатКлиентаФайл с путём к сертификату и паролем.

Отключать проверку сертификата целиком, лишь бы «заработало», нельзя: это открывает соединение для подмены. Если тестовый стенд отдаёт самоподписанный сертификат, добавьте именно его в доверенные, а не снимайте проверку.

Отладка и частые ошибки

Перед тем как винить платформу, проверьте запрос независимым инструментом — например, тем же телом и заголовками через консольную утилиту или Postman. Если там проходит, а в 1С нет, дело в коде на встроенном языке, а не в сервере.

Что помогает при отладке:

  • 🔎 Логируйте в журнал регистрации и код состояния, и тело ответа при ошибке — сервер почти всегда возвращает текст с причиной.
  • 🧪 Соберите внешнюю обработку с одной кнопкой и прогоняйте запрос в отладчике, глядя на свойства Ответ в табло.
  • 📝 Временно выведите готовое тело запроса (ТелоСтрока) в сообщение — убедитесь, что JSON валиден и кавычки не «съелись».

Типовые симптомы и причины:

Диагностика: выберите симптом

Частые вопросы

Чем POST отличается от GET в 1С технически?
GET вызывается методом Получить и не несёт тела — параметры идут в адресе ресурса. POST вызывается методом ОтправитьДляОбработки, и данные передаются в теле запроса через УстановитьТелоИзСтроки или УстановитьТелоИзДвоичныхДанных.
Как отправить PUT, PATCH или DELETE, если отдельного метода нет?
Методами Записать (PUT) и УдалитьДляОбработки (DELETE) либо универсальным ВызватьHTTPМетод("PATCH", Запрос) — первым параметром строка с нужным глаголом. Тело и заголовки собираются так же, как для POST.
Можно ли переиспользовать одно `HTTPСоединение` для нескольких запросов?
Да. Соединение создают один раз на хост, а объекты HTTPЗапрос — под каждый вызов. Это экономит время на повторную установку TLS-сессии при серии обращений к одному API.
Почему тело приходит на сервер пустым?
Чаще всего тело установили после отправки либо перепутали объект: тело ставится в HTTPЗапрос, а отправляется через HTTPСоединение. Проверьте, что УстановитьТелоИзСтроки вызван до ОтправитьДляОбработки.
Как увидеть сырой ответ сервера для разбора?
Вызовите Ответ.ПолучитьТелоКакСтроку() и выведите результат в сообщение или журнал регистрации. Код состояния смотрите в Ответ.КодСостояния, заголовки — в Ответ.Заголовки.