Почему SOAP-запрос через HTTPСоединение выдает ошибку 400, в то время как MSXML2.XMLHTTP работает корректно?

Программист 1С v8.3 (Обычные формы) IT и автоматизация бизнеса
← На главную

При интеграции 1С с внешними веб-сервисами (особенно использующими протокол SOAP) разработчики часто сталкиваются с ситуацией, когда нативный объект HTTPСоединение возвращает ошибку 400 Bad Request или 500 Internal Server Error, хотя аналогичный запрос через сторонние инструменты (Postman, браузер) или COM-объект MSXML2.XMLHTTP проходит успешно. Рассмотрим подробно, почему это происходит, какие "подводные камни" скрыты в реализации платформы и как правильно настроить обмен данными.

Анализ ситуации и сравнение подходов

Проанализируем типичный сценарий: нам необходимо отправить XML-пакет на сервер. В 1С это обычно реализуется через создание объекта HTTPЗапрос с последующей отправкой методом ОтправитьДляОбработки. Посмотрим на код, который часто приводит к ошибке:


HTTPСоединение = Новый HTTPСоединение("service.autopiter.ru");
HTTPЗаголовки = Новый Соответствие;
ТелоЗапроса = "<soap:Envelope>...</soap:Envelope>"; // XML-пакет

HTTPЗаголовки.Вставить("Content-Type", "text/xml; charset=utf-8");
HTTPЗаголовки.Вставить("Content-Length", СтрДлина(ТелоЗапроса)); // ОШИБКА ТУТ
HTTPЗаголовки.Вставить("SOAPAction", "http://tempuri.org/GetPrice");

HTTPЗапрос = Новый HTTPЗапрос("/v2/price", HTTPЗаголовки);
HTTPЗапрос.УстановитьТелоИзСтроки(ТелоЗапроса, , ИспользованиеByteOrderMark.НеИспользовать);
HTTPОтвет = HTTPСоединение.ОтправитьДляОбработки(HTTPЗапрос);

В то же время, использование устаревшего, казалось бы, COM-объекта MSXML2.XMLHTTP решает проблему "магическим" образом:


MSXML3 = Новый COMОбъект("MSXML2.XMLHTTP");
MSXML3.open("POST", "http://service.autopiter.ru/v2/price", 0);
MSXML3.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
MSXML3.send(ТелоЗапроса);

Выясним причины такого поведения, разобрав работу с заголовками и телом запроса по шагам.

Причина 1: Неправильный расчет Content-Length

Одной из самых распространенных ошибок является ручная установка заголовка Content-Length. Разберем, почему использование функции СтрДлина() в данном контексте фатально. Функция СтрДлина() возвращает количество символов в строке. Однако протокол HTTP требует, чтобы в заголовке длины указывалось количество байт.

Если ваша строка содержит кириллицу и кодируется в UTF-8, каждый русский символ занимает 2 байта. Таким образом, передавая значение СтрДлина(), вы сообщаете серверу, что данных будет почти в два раза меньше, чем отправляете на самом деле. Сервер, получив "лишние" данные или не дождавшись нужного объема, обрывает соединение с ошибкой 400.

Решение: Никогда не устанавливайте Content-Length вручную. При использовании метода УстановитьТелоИзСтроки() платформа 1С самостоятельно вычисляет размер данных в байтах и добавляет корректный заголовок автоматически.

Причина 2: Особенности обработки Cookie в платформе 1С

Проанализируем ситуацию, описанную в ходе обсуждения: сервер присылает несколько заголовков Set-Cookie. В 1С при получении ответа все одноименные заголовки объединяются в одно значение в объекте Соответствие, разделяясь запятыми.

Проблема заключается в том, что формат куки сам может содержать запятые (например, в поле expires=Sat, 24-Oct-2020). Когда мы пытаемся отправить эти куки обратно серверу в следующем запросе:

HTTPЗаголовки.Вставить("Cookie", СтрокаКукИзПредыдущегоОтвета);

Мы передаем серверу "кашу" из склеенных строк, которую веб-сервер не может корректно распарсить. MSXML2.XMLHTTP и другие системные библиотеки Windows (WinInet/WinHTTP) управляют сессионными куками автоматически на уровне операционной системы, соблюдая стандарты хранения и передачи. 1С же требует от разработчика "ручного" управления, если автоматическое управление в объекте HTTPСоединение (параметр ИспользоватьCookie) по каким-то причинам не срабатывает (например, из-за специфических настроек SameSite или HttpOnly).

Как проверить: Используйте сниффер трафика Fiddler. Он позволит увидеть "сырой" запрос, уходящий из 1С, и сравнить его с запросом из MSXML2. Вы увидите, что 1С может дублировать куки или обрезать их некорректно.

Причина 3: Скрытый заголовок "Expect: 100-continue"

Выясним еще одну техническую тонкость. Нативное HTTPСоединение под Windows работает через системную библиотеку WinHTTP. По умолчанию при отправке POST-запросов 1С может добавлять заголовок Expect: 100-continue. Этот заголовок заставляет клиент ждать подтверждения от сервера перед отправкой тела запроса.

Многие старые SOAP-серверы или балансировщики нагрузки (WAF) не понимают этот заголовок и возвращают 400 Bad Request. В 1С программного способа отключить этот заголовок через свойства объекта не существует. MSXML2.XMLHTTP этот заголовок не использует, что и объясняет его работоспособность.

Причина 4: User-Agent и блокировки на стороне сервера

Посмотрим на заголовок User-Agent. 1С всегда честно сообщает серверу, что запрос идет от 1C+Enterprise/8.3. Некоторые сервисы (например, Autopiter или другие агрегаторы запчастей) внедряют защиту от автоматизированного сбора данных (парсинга). Видя "одинэсный" агент, сервер может сразу выдавать ошибку 403 или 400, имитируя технический сбой.

Решение: Попробуйте явно подменить User-Agent на браузерный, например:


HTTPЗаголовки.Вставить("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36");

Как правильно организовать работу с SOAP через HTTPСоединение

Рассмотрим правильный алгоритм действий, если вы решили отказаться от COM-объектов в пользу нативного кода:

  1. Откажитесь от WS-ссылок: Если сервис "капризный", стандартный механизм WSПрокси может не подойти, так как он генерирует XML-пакет по жестким внутренним правилам платформы, которые сложно кастомизировать. Используйте HTTPСоединение.
  2. Формируйте тело запроса вручную: Используйте ЗаписьXML для создания тела SOAP-конверта, чтобы гарантировать чистоту структуры.
  3. Управляйте кодировкой: При использовании УстановитьТелоИзСтроки убедитесь, что кодировка совпадает с указанной в Content-Type (обычно utf-8).
  4. Обработка кук: Если сервер требует авторизации через Cookie, после первого запроса проанализируйте заголовок Set-Cookie. Если их несколько, выделите нужный (например, содержащий ID сессии) программно, как показано в примере ниже:

// Пример извлечения конкретной куки, если 1С их склеила некорректно
СтрокаКуки = HTTPОтвет.Заголовки["Set-Cookie"];
// Находим нужный фрагмент (например, AuthCoocie)
Позиция = СтрНайти(СтрокаКуки, "AuthCoocie");
Если Позиция > 0 Тогда
    КонечнаяТочка = СтрНайти(СтрокаКуки, ";", , Позиция);
    ВалиднаяКука = Сред(СтрокаКуки, Позиция, КонечнаяТочка - Позиция);
    HTTPЗаголовки.Вставить("Cookie", ВалиднаяКука);
КонецЕсли;

Выводы: что выбрать?

Проанализировав ситуацию, можно сделать вывод: MSXML2.XMLHTTP работает стабильнее в сложных случаях, потому что он более тесно интегрирован с сетевым стеком Windows и автоматически обрабатывает нюансы протокола (куки, сессии, TLS-протоколы), которые в 1С приходится реализовывать "руками".

Однако, если ваша цель — кроссплатформенность (работа под Linux), вам необходимо научить HTTPСоединение работать корректно. Для этого:

Только такой системный подход позволит заменить нестабильные COM-объекты на надежный нативный код 1С.

← На главную