При интеграции 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(ТелоЗапроса);
Выясним причины такого поведения, разобрав работу с заголовками и телом запроса по шагам.
Одной из самых распространенных ошибок является ручная установка заголовка Content-Length. Разберем, почему использование функции СтрДлина() в данном контексте фатально. Функция СтрДлина() возвращает количество символов в строке. Однако протокол HTTP требует, чтобы в заголовке длины указывалось количество байт.
Если ваша строка содержит кириллицу и кодируется в UTF-8, каждый русский символ занимает 2 байта. Таким образом, передавая значение СтрДлина(), вы сообщаете серверу, что данных будет почти в два раза меньше, чем отправляете на самом деле. Сервер, получив "лишние" данные или не дождавшись нужного объема, обрывает соединение с ошибкой 400.
Решение: Никогда не устанавливайте Content-Length вручную. При использовании метода УстановитьТелоИзСтроки() платформа 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С может дублировать куки или обрезать их некорректно.
Выясним еще одну техническую тонкость. Нативное HTTPСоединение под Windows работает через системную библиотеку WinHTTP. По умолчанию при отправке POST-запросов 1С может добавлять заголовок Expect: 100-continue. Этот заголовок заставляет клиент ждать подтверждения от сервера перед отправкой тела запроса.
Многие старые SOAP-серверы или балансировщики нагрузки (WAF) не понимают этот заголовок и возвращают 400 Bad Request. В 1С программного способа отключить этот заголовок через свойства объекта не существует. MSXML2.XMLHTTP этот заголовок не использует, что и объясняет его работоспособность.
Посмотрим на заголовок 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");
Рассмотрим правильный алгоритм действий, если вы решили отказаться от COM-объектов в пользу нативного кода:
WSПрокси может не подойти, так как он генерирует XML-пакет по жестким внутренним правилам платформы, которые сложно кастомизировать. Используйте HTTPСоединение.ЗаписьXML для создания тела SOAP-конверта, чтобы гарантировать чистоту структуры.УстановитьТелоИзСтроки убедитесь, что кодировка совпадает с указанной в Content-Type (обычно utf-8).Set-Cookie. Если их несколько, выделите нужный (например, содержащий ID сессии) программно, как показано в примере ниже:
// Пример извлечения конкретной куки, если 1С их склеила некорректно
СтрокаКуки = HTTPОтвет.Заголовки["Set-Cookie"];
// Находим нужный фрагмент (например, AuthCoocie)
Позиция = СтрНайти(СтрокаКуки, "AuthCoocie");
Если Позиция > 0 Тогда
КонечнаяТочка = СтрНайти(СтрокаКуки, ";", , Позиция);
ВалиднаяКука = Сред(СтрокаКуки, Позиция, КонечнаяТочка - Позиция);
HTTPЗаголовки.Вставить("Cookie", ВалиднаяКука);
КонецЕсли;
Проанализировав ситуацию, можно сделать вывод: MSXML2.XMLHTTP работает стабильнее в сложных случаях, потому что он более тесно интегрирован с сетевым стеком Windows и автоматически обрабатывает нюансы протокола (куки, сессии, TLS-протоколы), которые в 1С приходится реализовывать "руками".
Однако, если ваша цель — кроссплатформенность (работа под Linux), вам необходимо научить HTTPСоединение работать корректно. Для этого:
Content-Length.Только такой системный подход позволит заменить нестабильные COM-объекты на надежный нативный код 1С.