Как убрать паразитный символ "?" в начале POST-запроса и исправить ошибку разбора XML в 1С?

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

При интеграции 1С с внешними системами (особенно на базе Oracle, Java или PHP) разработчики часто сталкиваются с загадочной проблемой: запрос, который идеально работает в сторонних утилитах вроде Postman или Advanced Rest Client, выдает ошибку при отправке из 1С. Чаще всего принимающая сторона возвращает сообщение о сбое разбора данных, например: error ORA-31011: сбой разбора XML. При детальном анализе трафика выясняется, что в самом начале передаваемого текста появляется «паразитный» символ вопроса или точка.

В этой статье мы подробно разберем, откуда берется этот символ, почему стандартные методы записи файлов в 1С могут мешать обмену данными и как правильно сформировать HTTP-запрос, чтобы сервер его принял.

Выясняем причину: что такое BOM?

Разберем природу «паразитного символа». На самом деле это не знак вопроса, а BOM (Byte Order Mark) — специальная метка последовательности байтов. Для кодировки UTF-8 эта метка выглядит как последовательность трех байтов: EF BB BF.

Многие текстовые редакторы Windows и сама платформа 1С по умолчанию добавляют эти байты в начало текстового файла, чтобы приложения могли однозначно определить: перед ними текст в кодировке UTF-8. Однако стандарты веб-протоколов и многие строгие XML-парсеры (как в базе данных Oracle) не ожидают увидеть ничего, кроме самого XML-декларирования или корневого тега. Получив в начале потока данных EF BB BF, сервер интерпретирует их как некорректные символы и обрывает обработку с ошибкой разбора.

Проблема использования временных файлов

Рассмотрим типичный сценарий, с которого часто начинают разработчики 1С. Посмотрим на пример кода, который гарантированно приведет к появлению BOM:


Функция ВыполнитьPOSTЗапрос(ИмяСервера, Порт, Команда, СсылкаНаРесурс)
    HTTP = Новый HTTPСоединение(ИмяСервера, Порт, , , , 60);
    ФайлЗапроса = ПолучитьИмяВременногоФайла();
    ТекстовыйФайл = Новый ТекстовыйДокумент;
    ТекстовыйФайл.УстановитьТекст(СокрЛП(Команда));
    // Ошибка здесь: метод Записать с этой кодировкой всегда добавит BOM
    ТекстовыйФайл.Записать(ФайлЗапроса, КодировкаТекста.UTF8); 
    
    ФайлРезультата = ПолучитьИмяВременногоФайла();
    ЗаголовокHTTP = Новый Соответствие();
    ЗаголовокHTTP.Вставить("Content-Type", "application/xml; charset=utf-8");
    
    HTTP.ОтправитьДляОбработки(ФайлЗапроса, СсылкаНаРесурс, ФайлРезультата, ЗаголовокHTTP);
    // ... дальнейшая обработка
КонецФункции

В данном примере объект ТекстовыйДокумент при вызове метода Записать принудительно вставляет метку BOM в начало файла. Даже если вы не видите её в обычном блокноте, она физически присутствует в файле и улетает на сервер. Использование временных файлов для формирования тела запроса — это «карма», которая часто приводит к подобным ошибкам. Проанализируем более современные способы решения задачи.

Ловушка заголовка Content-Length

Еще одна критическая ошибка при ручном формировании запроса — попытка самостоятельно вычислить и установить заголовок Content-Length. Рассмотрим, почему это опасно:


// ОШИБОЧНЫЙ ПУТЬ
ЗаголовокЗапросаHTTP.Вставить("Content-Length", СтрДлина(СтрокаОтправки)); 

Проанализируем ситуацию: функция СтрДлина возвращает количество символов в строке. Однако заголовок Content-Length должен содержать количество байтов. В кодировке UTF-8 один символ кириллицы занимает 2 байта. Если в вашей строке есть русские буквы, значение СтрДлина будет почти в два раза меньше реального размера данных в байтах. Сервер получит обрезанный текст и вернет ошибку 400 или 500.

Важное правило: никогда не устанавливайте Content-Length вручную. При использовании объекта HTTPЗапрос платформа 1С сделает это автоматически и корректно, вычислив размер данных после их преобразования в двоичный вид.

Правильное решение: использование HTTPЗапрос

Теперь разберем, как правильно сформировать тело запроса без лишних байтов и файлов. Начиная с современных версий платформы 8.3, у метода УстановитьТелоИзСтроки появился третий параметр, управляющий BOM.

Рассмотрим итоговый, правильный вариант функции:


Функция ВыполнитьPOSTЗапрос(ИмяСервера, Порт, СтрокаОтправки, СсылкаНаРесурс) Экспорт
    
    // 1. Создаем соединение без лишних параметров
    HTTP = Новый HTTPСоединение(ИмяСервера, Порт, , , , 60);
        
    // 2. Указываем заголовки. Content-Length НЕ ПИШЕМ!
    ЗаголовокЗапросаHTTP = Новый Соответствие();
    ЗаголовокЗапросаHTTP.Вставить("Content-Type", "application/xml; charset=utf-8"); 
        
    // 3. Создаем объект запроса
    HTTPЗапрос = Новый HTTPЗапрос(СсылкаНаРесурс, ЗаголовокЗапросаHTTP);
    
    // 4. КЛЮЧЕВОЙ МОМЕНТ: Указываем ИспользованиеByteOrderMark.НеИспользовать
    // Это гарантирует отсутствие "паразитного символа" в начале данных
    HTTPЗапрос.УстановитьТелоИзСтроки(СтрокаОтправки, КодировкаТекста.UTF8, ИспользованиеByteOrderMark.НеИспользовать);
    
    // 5. Отправляем запрос и получаем результат в одну строку
    Попытка
        Ответ = HTTP.ОтправитьДляОбработки(HTTPЗапрос);
        Возврат Ответ.ПолучитьТелоКакСтроку();
    Исключение
        ЗаписьЖурналаРегистрации("Ошибка HTTP", УровеньЖурналаРегистрации.Ошибка, , , ОписаниеОшибки());
        Возврат Неопределено;
    КонецПопытки;
        
КонецФункции

Альтернативный способ (через ДвоичныеДанные)

Если вы работаете на очень старой версии платформы, где у метода УстановитьТелоИзСтроки нет управления BOM, или хотите иметь 100% контроль над байтами, воспользуйтесь преобразованием в двоичные данные. Метод ПолучитьДвоичныеДанныеИзСтроки также позволяет явно отключить BOM.

Разберем этот алгоритм:

  1. Преобразуем исходную строку в двоичные данные, указав кодировку и флаг отсутствия BOM.
  2. Передаем полученный объект в тело запроса через метод УстановитьТелоИзДвоичныхДанных.

// Пример для гарантированного удаления BOM на любых версиях
ТелоВБайтах = ПолучитьДвоичныеДанныеИзСтроки(СтрокаОтправки, КодировкаТекста.UTF8, Ложь); 
HTTPЗапрос.УстановитьТелоИзДвоичныхДанных(ТелоВБайтах);

Здесь третий параметр Ложь в функции ПолучитьДвоичныеДанныеИзСтроки как раз отвечает за то, что метка Byte Order Mark не будет добавлена в начало массива байтов.

Подведем итоги

Для успешной отправки XML или JSON из 1С во внешние системы придерживайтесь следующих рекомендаций:

Соблюдение этих правил позволит вам избежать большинства проблем с интеграцией и обеспечит корректную работу ваших обработок с любыми внешними веб-сервисами.

← На главную