При интеграции 1С с внешними веб-сервисами (например, системами электронных фактур или государственными порталами) разработчики часто сталкиваются с ситуацией, когда стандартный объект HTTPСоединение возвращает ошибку 400 Bad Request. Особенно часто это происходит при передаче сложных XML-структур, где один из атрибутов или узлов содержит вложенный XML, обернутый в секцию ![CDATA[ ... ]].
Интересно, что тот же самый запрос, отправленный через COM-объект MSXML2.XMLHTTP, может отрабатывать корректно. Это наводит на мысль о нюансах реализации протокола HTTP в самой платформе 1С. В данной статье мы разберем, почему возникает эта проблема, проанализируем влияние кодировок и специальных символов, а также составим пошаговую инструкцию по правильной настройке HTTPЗапроса.
Код состояния 400 Bad Request означает, что сервер не смог обработать запрос из-за некорректного синтаксиса. При работе с XML в 1С основными «виновниками» этой ошибки являются:
?xml.HTTPСоединение в 1С требует явного указания Content-Type.Проанализируем ситуацию, описанную в практике: замена кодировки на ANSI при установке тела запроса помогла решить проблему. Рассмотрим, как правильно использовать метод УстановитьТелоИзСтроки для исключения ошибок синтаксиса.
Синтаксис метода выглядит следующим образом:
УстановитьТелоИзСтроки(<ТелоКакСтрока>, <Кодировка>, <ИспользоватьBOM>)
Разберем по шагам правильный алгоритм формирования запроса:
Шаг 1. Отключение BOM. Это критически важный момент. Чтобы веб-сервер не «споткнулся» о начальные байты строки, мы должны явно указать 1С не использовать BOM. Используем для этого системное перечисление ИспользованиеByteOrderMark.НеИспользовать.
Шаг 2. Выбор правильной кодировки. Хотя стандартом для XML является UTF-8, некоторые сервера (особенно работающие на устаревших стеках технологий или настроенные под специфические региональные стандарты) лучше принимают ANSI (Windows-1251). Если ваш запрос с UTF-8 не проходит, попробуйте явно указать КодировкаТекста.ANSI.
Посмотрим на пример кода, который решает проблему:
// Формируем текст XML запроса
ТекстXML = "<Root><Data><![CDATA[" + ВложенныйXML + "]]></Data></Root>";
// Подготавливаем запрос
Запрос = Новый HTTPЗапрос("/api/efactura");
Запрос.Заголовки.Вставить("Content-Type", "application/xml; charset=utf-8");
// Устанавливаем тело запроса с явным указанием параметров
// Используем ANSI и отключаем BOM
Запрос.УстановитьТелоИзСтроки(ТекстXML, КодировкаТекста.ANSI, ИспользованиеByteOrderMark.НеИспользовать);
// Выполняем отправку
Ответ = Соединение.ОтправитьДляОбработки(Запрос);
Многие разработчики задаются вопросом: почему старый метод через COMОбъект работает «из коробки»? Ответ кроется в том, что MSXML2.XMLHTTP является оберткой над системными библиотеками Windows (WinHTTP/WinInet). Эти библиотеки автоматически управляют многими низкоуровневыми аспектами:
Content-Length.В 1С при использовании HTTPСоединение мы работаем на более низком уровне и должны контролировать каждый байт передаваемой информации. Поэтому ручная настройка кодировки и отключение BOM становятся обязательными при работе со строгими XML-парсерами.
Если в вашем XML-запросе есть вложенный XML внутри CDATA, проанализируем ситуацию с точки зрения корректности данных:
]]>, внешняя секция CDATA будет закрыта раньше времени. Это гарантированно приведет к ошибке 400. Убедитесь, что данные внутри CDATA не содержат этой последовательности.ЗаписьXML. Он автоматически позаботится о правильном формировании секций CDATA и спецсимволов.Рассмотрим пример правильного формирования XML с вложенной секцией:
Запись = Новый ЗаписьXML;
Запись.УстановитьСтроку();
Запись.ЗаписатьОбъявлениеXML();
Запись.ЗаписатьНачалоЭлемента("Request");
Запись.ЗаписатьНачалоЭлемента("Content");
// Записываем вложенный XML как текст в секции CDATA
Запись.ЗаписатьСекциюCDATA(ВложенныйXMLТекст);
Запись.ЗаписатьКонецЭлемента(); // Content
Запись.ЗаписатьКонецЭлемента(); // Request
РезультатXML = Запись.Закрыть();
Метод ОтправитьДляОбработки считается устаревшим в современных версиях платформы 8.3. Для более стабильной работы и детального контроля рекомендуется использовать метод ВызватьHTTPМетод.
Разберем пример современного подхода:
// Создаем соединение с поддержкой OpenSSL
Защита = Новый ЗащищенноеСоединениеOpenSSL();
Соединение = Новый HTTPСоединение("example.com", 443, , , , , Защита);
// Создаем запрос
Запрос = Новый HTTPЗапрос("/api/v1/data");
Запрос.Заголовки.Вставить("Content-Type", "text/xml; charset=windows-1251");
// Устанавливаем тело без BOM
Запрос.УстановитьТелоИзСтроки(ТекстЗапроса, "windows-1251", ИспользованиеByteOrderMark.НеИспользовать);
// Вызываем метод POST
Ответ = Соединение.ВызватьHTTPМетод("POST", Запрос);
Если Ответ.КодСостояния <> 200 Тогда
Сообщить("Ошибка запроса: " + Ответ.КодСостояния);
Сообщить(Ответ.ПолучитьТелоКакСтроку());
КонецЕсли;
Резюме: Если вы получаете ошибку 400 при отправке XML, первым делом попробуйте явно отключить BOM и поэкспериментируйте с кодировками (UTF-8 vs ANSI). В большинстве случаев проблема кроется именно в этих невидимых байтах, которые 1С добавляет к строке по умолчанию.