Интеграции в 1С

Ошибка выполнения POST-запроса в 1С: как найти причину за 5 минут

Коротко

Ошибка выполнения POST-запроса в 1С — это либо исключение платформы (не удалось установить соединение: SSL/TLS, таймаут, DNS, прокси), либо ненулевой код состояния в объекте HTTPОтвет (400, 401, 403, 404, 415, 500 и т.д.). Первое ловится через Попытка/Исключение, второе — проверкой Ответ.КодСостояния и чтением тела через Ответ.ПолучитьТелоКакСтроку("UTF-8").

Универсальный шаблон: обернуть ОтправитьДляОбработки() в Попытка, при успехе — проверить код и, если он не 2xx, залогировать тело ответа в журнал регистрации. Без логирования тела ошибка post 1с превращается в гадание.

Исключение vs код состояния: два типа ошибок POST в 1С

Когда говорят «post запрос ошибка 1с», в реальности путают две принципиально разные ситуации. От того, какая из них произошла, зависит и способ диагностики, и что именно чинить в коде обработки.

  • Исключение платформы. Соединение не установлено или прервано: SSL-рукопожатие не прошло, недоступен DNS, не отвечает порт, истёк таймаут, не пускает прокси. В этом случае метод ОтправитьДляОбработки() выбрасывает исключение — объекта HTTPОтвет не будет.
  • Ошибка HTTP-уровня. Соединение прошло, сервер ответил, но КодСостояния не 2xx: 400 — плохое тело, 401 — авторизация, 403 — права, 404 — путь, 415 — Content-Type, 500 — сервер упал. Исключения нет, HTTPОтвет есть, и вся диагностика — в его теле и заголовках.

Правильный код обрабатывает обе ветки:

Попытка
    Ответ = Соединение.ОтправитьДляОбработки(Запрос);
Исключение
    // Ветка 1: соединение не удалось — SSL, DNS, таймаут, прокси
    ЗаписьЖурналаРегистрации(
        "Интеграция.POST",
        УровеньЖурналаРегистрации.Ошибка,
        , ,
        "Не удалось выполнить POST: " + ОписаниеОшибки());
    ВызватьИсключение;
КонецПопытки;

Если Ответ.КодСостояния < 200 ИЛИ Ответ.КодСостояния >= 300 Тогда
    // Ветка 2: сервер ответил, но с ошибкой
    ТелоОтвета = Ответ.ПолучитьТелоКакСтроку("UTF-8");
    ЗаписьЖурналаРегистрации(
        "Интеграция.POST",
        УровеньЖурналаРегистрации.Ошибка,
        , ,
        "HTTP " + Ответ.КодСостояния + ": " + ТелоОтвета);
КонецЕсли;

Ошибка соединения: SSL, «невосстановимая ошибка POST к ресурсу», таймаут

Классический текст исключения в 1С — «Невосстановимая ошибка при выполнении POST к ресурсу /...». Он означает, что до сервера дозвониться не удалось. Причины по частоте:

  • HTTPS без ЗащищенноеСоединениеOpenSSL. Самая частая причина. При работе с https:// обязательно передавать объект защиты в конструктор HTTPСоединение, иначе платформа не сможет провести TLS-рукопожатие.
  • Таймаут. Сервер долго отвечает — соединение обрывается по умолчанию. Явно задавайте таймаут в конструкторе.
  • Сертификат. Самоподписанный сертификат сервера или устаревший корневой — платформа отклоняет соединение.
  • DNS и прокси. Имя не резолвится с сервера 1С, либо корпоративный прокси не пускает исходящий HTTPS.
ЗащитаHTTPS = Новый ЗащищенноеСоединениеOpenSSL;
Соединение = Новый HTTPСоединение(
    "api.example.com",       // Сервер (без https:// и без пути)
    443,                     // Порт
    ,                        // Пользователь (для Basic Auth)
    ,                        // Пароль
    ,                        // Прокси
    60,                      // Таймаут, сек
    ЗащитаHTTPS);            // ЗащищенноеСоединение
Новый HTTPСоединение
Сервертолько хост, без схемы и пути
Порт443 для https, 80 для http
Пользователь / ПарольBasic Auth
Проксиобъект InternetProxy
Таймаутсекунды на всю операцию
ЗащищенноеСоединениеЗащищенноеСоединениеOpenSSL для https
Если POST падает только на проде и работает локально — почти всегда виноват прокси или закрытый исходящий порт 443 с сервера 1С. Проверьте telnet api.example.com 443 прямо с машины, где живёт кластер 1С.

Как корректно отправить POST-запрос в 1С

Половина ошибок «1с ошибка выполнении post запроса» — от неправильного формирования самого запроса. Правильный шаблон для JSON:

Запрос = Новый HTTPЗапрос("/api/v1/documents");
Запрос.Заголовки.Вставить("Content-Type", "application/json; charset=utf-8");
Запрос.Заголовки.Вставить("Accept", "application/json");
Запрос.Заголовки.Вставить("Authorization", "Bearer " + Токен);

// ВАЖНО: третий параметр = Ложь → без BOM. Многие сервера падают с 400 из-за BOM.
Запрос.УстановитьТелоИзСтроки(ТелоJSON, КодировкаТекста.UTF8, ИспользованиеByteOrderMark.НеИспользовать);

Ответ = Соединение.ОтправитьДляОбработки(Запрос);
HTTPЗапрос.УстановитьТелоИзСтроки
ТелоКакСтрокаподготовленное тело запроса
Кодировкаобычно КодировкаТекста.UTF8
ИспользоватьBOMНеИспользовать для чистого UTF-8
→ метод, ничего не возвращает

Content-Length платформа считает сама из тела — руками его выставлять не нужно и вредно (частая причина 400). Если сервер требует form-urlencoded — заголовок ставится application/x-www-form-urlencoded, а тело формируется как строка key=value&key2=value2 с URL-кодированием значений.

Ошибка 400 Bad Request: тело, JSON и BOM

Код 400 означает: сервер получил запрос, разобрал заголовки, но не смог обработать тело. В 90% случаев причина — на стороне 1С.

  • Невалидный JSON. Собран строкой вручную, потерялась запятая, лишний trailing comma, незакрытая скобка. Всегда собирайте JSON через ЗаписьJSON и ЗаписатьJSON(), не через конкатенацию строк.
  • ByteOrderMark в начале тела. Если написать УстановитьТелоИзСтроки(Тело, КодировкаТекста.UTF8) без третьего параметра, платформа добавит BOM (символ \uFEFF), и строгий парсер JSON на сервере ответит 400. Всегда передавайте ИспользованиеByteOrderMark.НеИспользовать.
  • Не заполнены обязательные поля. Сервер ждёт customer_id, а его в теле нет. Читайте документацию API и смотрите тело ответа — часто там JSON с точным списком ошибок валидации.
  • Неправильная кодировка. Кириллица без указания КодировкаТекста.UTF8 уходит в ANSI и превращается в мусор.
// Пример: сборка JSON без BOM и без ручной конкатенации
ПотокЗаписи = Новый ЗаписьJSON;
ПотокЗаписи.УстановитьСтроку();
Данные = Новый Структура("customer_id, amount, currency", 12345, 100.50, "RUB");
ЗаписатьJSON(ПотокЗаписи, Данные);
ТелоJSON = ПотокЗаписи.Закрыть();

Запрос.УстановитьТелоИзСтроки(
    ТелоJSON,
    КодировкаТекста.UTF8,
    ИспользованиеByteOrderMark.НеИспользовать);

Ошибки 401 и 403: авторизация и права

Внешне похожи, чинятся по-разному.

  • 401 Unauthorized — сервер не понял, кто вы. Не передан заголовок Authorization, истёк Bearer-токен, неверный логин/пароль для Basic Auth, не подписан запрос там, где нужна HMAC-подпись.
  • 403 Forbidden — сервер вас узнал, но эта учётка не имеет прав на этот ресурс. IP не в whitelist, роль без разрешения, ограничение по региону/сегменту.
// Bearer-токен: правильно продлевать при 401 и повторять запрос
Ответ = Соединение.ОтправитьДляОбработки(Запрос);
Если Ответ.КодСостояния = 401 Тогда
    Токен = ПолучитьНовыйТокен();  // ваша функция обновления
    Запрос.Заголовки.Вставить("Authorization", "Bearer " + Токен);
    Ответ = Соединение.ОтправитьДляОбработки(Запрос);
КонецЕсли;

// Basic Auth задаётся не заголовком, а в конструкторе HTTPСоединение:
Соединение = Новый HTTPСоединение("api.example.com", 443, "login", "password", , 60, ЗащитаHTTPS);

Для Basic Auth заголовок Authorization: Basic ... платформа формирует сама, если переданы Пользователь и Пароль в конструктор. Дублировать его руками не нужно.

Ошибки 404 и 415: путь и Content-Type

  • 404 Not Found — сервер не нашёл ресурс по указанному пути. В HTTPЗапрос("/api/v1/documents") должен быть только путь, без https://api.example.com в начале. Хост уже задан в HTTPСоединение. Второй частый повод — API-версия: путь /api/v1/... заменили на /api/v2/....
  • 415 Unsupported Media Type — не подходит заголовок Content-Type. Отправили JSON без application/json, или form-urlencoded с указанием JSON. Проверяйте документацию API: некоторые сервера ждут строго application/json; charset=utf-8, а варианту text/json отвечают 415.
// Разделение хоста и пути — типовая ошибка
Соединение = Новый HTTPСоединение("api.example.com", 443, , , , 60, ЗащитаHTTPS);
Запрос = Новый HTTPЗапрос("/api/v1/documents");   // Правильно
// Запрос = Новый HTTPЗапрос("https://api.example.com/api/v1/documents"); // 404 или ошибка соединения

Ошибка 500 Internal Server Error: не наша вина, но логируем

Код 500 означает, что сервер упал на своей стороне при обработке нашего запроса. Это не ошибка 1С, но:

  • Тело ответа при 500 часто содержит текст исключения или его хеш — обязательно логируйте его целиком в журнал регистрации, иначе разработчику API нечего искать в своих логах.
  • На 5xx имеет смысл сделать ретрай с задержкой — сервер мог временно упасть или уйти на рестарт. 4xx повторять бессмысленно: ответ не изменится, пока не поправите запрос.
МаксимумПопыток = 3;
Задержка = 2; // секунды

Для Попытка = 1 По МаксимумПопыток Цикл
    Ответ = Соединение.ОтправитьДляОбработки(Запрос);

    Если Ответ.КодСостояния >= 200 И Ответ.КодСостояния < 300 Тогда
        Прервать; // успех
    ИначеЕсли Ответ.КодСостояния >= 500 И Попытка < МаксимумПопыток Тогда
        // ретрай только для 5xx
        ЗаписьЖурналаРегистрации(
            "Интеграция.POST",
            УровеньЖурналаРегистрации.Предупреждение, , ,
            "HTTP " + Ответ.КодСостояния + ", попытка " + Попытка);
        // экспоненциальная задержка: 2, 4, 8 секунд
        Приостановить(Задержка * Попытка * 1000);
    Иначе
        Прервать; // 4xx или последняя попытка — выходим
    КонецЕсли;
КонецЦикла;
Ретраить POST безопасно только если API идемпотентен (или поддерживает Idempotency-Key). Иначе повторная отправка может создать дубль документа на стороне контрагента.

Без чтения тела ответа диагностировать ошибку POST в 1С невозможно — вы работаете вслепую. Тело читается через ПолучитьТелоКакСтроку() объекта HTTPОтвет, кодировку берут из заголовка Content-Type ответа (обычно UTF-8, иногда windows-1251 — в этом случае так и передавайте, иначе получите кракозябры вместо сообщения об ошибке).

ТелоОтвета = Ответ.ПолучитьТелоКакСтроку("UTF-8");
СерверскийТрейс = Ответ.Заголовки.Получить("X-Request-Id");

ЗаписьЖурналаРегистрации(
    "Интеграция.POST",
    УровеньЖурналаРегистрации.Ошибка, , ,
    "HTTP " + Ответ.КодСостояния + " | X-Request-Id: " + СерверскийТрейс + Символы.ПС + ТелоОтвета);
HTTPОтвет
КодСостояниясвойство: HTTP-код 200/400/500 и т.д.
Заголовкисвойство: соответствие имя → значение
ПолучитьТелоКакСтроку(<Кодировка>)тело как строка в заданной кодировке
ПолучитьТелоКакДвоичныеДанные()тело как ДвоичныеДанные (для файлов)

Некоторые API требуют сначала авторизоваться через POST /login, получить Set-Cookie, и все последующие POST-запросы отправлять с этим Cookie. Платформа 1С Cookie сама не хранит между вызовами ОтправитьДляОбработки() — их нужно доставать из заголовков ответа и подставлять в заголовки следующего запроса вручную.

Некоторые API требуют сначала авторизоваться через POST /login, получить Set-Cookie, и все последующие POST-запросы отправлять с этим Cookie. Платформа 1С Cookie сама не хранит между вызовами ОтправитьДляОбработки() — их нужно доставать из заголовков ответа и подставлять в заголовки следующего запроса вручную.

// Первый POST: авторизация, забираем Cookie
ОтветЛогин = Соединение.ОтправитьДляОбработки(ЗапросЛогин);
Кука = ОтветЛогин.Заголовки.Получить("Set-Cookie");
Если Кука <> Неопределено Тогда
    // Отрезаем всё после ";" — атрибуты Path/Expires серверу отправлять не нужно
    Позиция = СтрНайти(Кука, ";");
    Если Позиция > 0 Тогда
        Кука = Лев(Кука, Позиция - 1);
    КонецЕсли;
    ЗапросДанных.Заголовки.Вставить("Cookie", Кука);
КонецЕсли;

// Второй POST: с сохранённой Cookie
ОтветДанные = Соединение.ОтправитьДляОбработки(ЗапросДанных);

Частые ошибки при отправке POST-запроса в 1С

  • HTTPS без ЗащищенноеСоединениеOpenSSL. Первая причина «невосстановимой ошибки POST к ресурсу». Всегда создавайте Новый ЗащищенноеСоединениеOpenSSL и передавайте в конструктор.
  • Схема в адресе ресурса. В HTTPЗапрос(...) должен быть только путь"/api/v1/orders", а не "https://host/api/v1/orders". Иначе 404 или ошибка соединения.
  • BOM в теле JSON. Без третьего параметра УстановитьТелоИзСтроки() добавляет BOM и получает 400. Всегда ИспользованиеByteOrderMark.НеИспользовать.
  • Игнорирование тела ответа при ошибке. Проверяете только КодСостояния и не логируете ПолучитьТелоКакСтроку() — теряете точное сообщение сервера и разбираетесь наугад.
  • Ретрай на 4xx. 401, 403, 404, 415 повторять бессмысленно — ответ не изменится. Ретраить нужно только 5xx и таймауты.
  • Basic Auth дублируется заголовком. Если пользователь и пароль переданы в конструктор HTTPСоединение, ставить Authorization: Basic ... руками не нужно — платформа сделает это сама.
  • Отсутствие Попытка/Исключение. Сетевые ошибки — норма, а не аварийная ситуация. Без Попытка/Исключение/ОписаниеОшибки() интеграция валит регламентное задание целиком при любом сетевом сбое.

Быстрый чек-лист, когда ошибка уже произошла и надо найти причину за 5 минут:

  1. Отделить исключение (соединение не удалось) от кода состояния (сервер ответил).
  2. Для https — есть ли ЗащищенноеСоединениеOpenSSL в конструкторе?
  3. В HTTPЗапрос() передан путь, а не полный URL?
  4. Заголовок Content-Type точно соответствует ожиданиям API?
  5. Тело собрано без BOM (ИспользованиеByteOrderMark.НеИспользовать)?
  6. Авторизация: токен свежий, Basic Auth не дублируется в заголовке?
  7. Логируется ли тело ответа при не-2xx, есть ли X-Request-Id в логах?
  8. Для 5xx включён ретрай с задержкой и идемпотентностью?

Частые вопросы об ошибке POST-запроса в 1С

Что значит «Невосстановимая ошибка при выполнении POST к ресурсу» в 1С?

Это исключение платформы: 1С не смогла установить или удержать соединение с сервером. В 80% случаев причина — HTTPS без объекта ЗащищенноеСоединениеOpenSSL в конструкторе HTTPСоединение, реже — таймаут, DNS, прокси или закрытый порт 443 с сервера 1С. Объекта HTTPОтвет в этом случае нет — диагностика только через ОписаниеОшибки() и журнал регистрации.

Почему сервер отвечает 400 на валидный, на первый взгляд, JSON?

Три классические причины. Первая — BOM в начале тела: происходит при УстановитьТелоИзСтроки(Тело, КодировкаТекста.UTF8) без третьего параметра, лечится ИспользованиеByteOrderMark.НеИспользовать. Вторая — JSON собран конкатенацией строк и содержит невидимую опечатку: используйте ЗаписьJSON. Третья — не заполнено обязательное поле; читайте тело ответа через Ответ.ПолучитьТелоКакСтроку("UTF-8"), там обычно указан точный список ошибок валидации.

Как правильно ретраить POST при ошибке 500 в 1С?

Повторять только коды 5xx и сетевые исключения, а 4xx — никогда. Между попытками — экспоненциальная задержка (2, 4, 8 секунд), не более 3–5 попыток. Если API не идемпотентен, добавляйте заголовок Idempotency-Key с уникальным ключом операции, иначе повтор может создать дубль документа у контрагента.