Почему 1С получает «кракозябры» вместо нормального ответа от веб-сервиса и как это исправить?

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

При интеграции 1С с внешними веб-сервисами по протоколу HTTP разработчики часто сталкиваются с неприятной ситуацией: запрос отправляется успешно, сервер возвращает статус 200 OK, но вместо ожидаемой структуры XML или JSON в тело ответа приходит непонятный набор бинарных символов («кракозябр»). При этом в сторонних утилитах тестирования (например, в SoapUI или Postman) этот же запрос возвращает абсолютно читаемый и корректный текст.

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

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

Давайте детально рассмотрим ситуацию, описанную на форуме. Разработчик отправляет GET-запрос к стороннему веб-сервису через объект HTTPСоединение. Пытаясь получить ответ как строку с помощью метода ПолучитьТелоКакСтроку(КодировкаТекста.UTF8), он видит хаотичное скопление спецсимволов. Настройка различных кодировок в параметрах метода не приносит результата.

Если проанализировать RAW-заголовки успешного ответа, полученного в SoapUI, мы увидим ключевую строчку:


HTTP/1.1 200 OK
Date: Wed, 05 Feb 2020 06:24:57 GMT
Content-Type: application/json; charset=utf-8
Content-Encoding: gzip
Vary: Accept-Encoding

Обратите внимание на заголовок Content-Encoding: gzip. Он указывает на то, что сервер сжал текстовый JSON-ответ с помощью алгоритма GZIP для экономии сетевого трафика. Программы SoapUI и Postman под капотом автоматически распаковывают такой поток данных «на лету» перед тем, как отобразить его пользователю. Платформа 1С при вызове метода ПолучитьТелоКакСтроку() этого не делает — она честно пытается интерпретировать бинарный сжатый gzip-архив как обычный текст в кодировке UTF-8, что и приводит к искажению данных.

Решение №1: Отключение сжатия со стороны сервера (Accept-Encoding: identity)

Самый простой и быстрый способ решить проблему, не требующий написания сложных алгоритмов распаковки архивов, — явно указать веб-сервису, что 1С не умеет работать со сжатыми данными. Для этого используется HTTP-заголовок Accept-Encoding.

По умолчанию, если заголовок не задан, сервер может принудительно сжимать данные. Если мы передадим значение identity, это будет означать требование прислать ответ «как есть», то есть в несжатом текстовом виде.

Рассмотрим пример кода, реализующий этот подход:


// Создаем соединение с сервером
Соединение = Новый HTTPСоединение("dataaccess.scania.com",,,,, 30, Новый ЗащищенноеСоединениеOpenSSL());

// Формируем запрос
ПараметрыЗапроса = "startDate=20200102&vinOfInterest=TT00068&endDate=20200131";
Запрос = Новый HTTPЗапрос("/?" + ПараметрыЗапроса);

// Указываем заголовки. 
// ВАЖНО: Заголовок "charset" в GET-запросе не имеет смысла для тела ответа,
// поэтому мы управляем сжатием через Accept-Encoding.
Запрос.Заголовки.Вставить("Accept-Encoding", "identity");

// Отправляем запрос
Ответ = Соединение.Получить(Запрос);

Если Ответ.КодСостояния = 200 Тогда
    // Теперь сервер вернет обычный текст JSON, и метод отработает корректно
    ТекстОтвета = Ответ.ПолучитьТелоКакСтроку(КодировкаТекста.UTF8);
    Сообщить(ТекстОтвета);
Иначе
    Сообщить("Ошибка запроса! Код состояния: " + Ответ.КодСостояния);
КонецЕсли;

Преимущества метода: простота реализации, не требуются дополнительные компоненты или сложные вычисления.

Недостатки метода: отказ от сжатия увеличивает объем передаваемого трафика. Если веб-сервис передает мегабайты XML- или JSON-данных, скорость их получения может существенно снизиться.

Решение №2: Использование возможностей платформы 1С 8.3.26 и новее

Если вы работаете на современной версии платформы 1С (начиная с релиза 8.3.26), у вас есть возможность использовать встроенные средства для работы с популярными форматами сжатия, включая GZIP.

В платформе появился новый объект ЧтениеФайлаАрхива, который заменил устаревший ЧтениеZipФайла и добавил нативную поддержку распаковки GZIP. Теперь мы можем согласиться на сжатие данных ради экономии трафика, получить двоичный файл архива и распаковать его штатными методами.

Проанализируем пример реализации:


Соединение = Новый HTTPСоединение("dataaccess.scania.com",,,,, 30, Новый ЗащищенноеСоединениеOpenSSL());
Запрос = Новый HTTPЗапрос("/?startDate=20200102&vinOfInterest=TT00068&endDate=20200131");

// Разрешаем серверу сжимать данные
Запрос.Заголовки.Вставить("Accept-Encoding", "gzip, deflate");

Ответ = Соединение.Получить(Запрос);

Если Ответ.КодСостояния = 200 Тогда
    // Проверяем, действительно ли сервер применил сжатие
    ТипСжатия = Ответ.Заголовки.Получить("Content-Encoding");
    
    Если ТипСжатия = "gzip" Тогда
        // Получаем тело ответа как двоичные данные
        СжатыеДанные = Ответ.ПолучитьТелоКакДвоичныеДанные();
        
        // Записываем сжатые данные во временный файл
        ИмяВременногоАрхива = ПолучитьИмяВременногоФайла("gz");
        СжатыеДанные.Записать(ИмяВременногоАрхива);
        
        // Создаем объект для чтения архива (доступно с 8.3.26)
        Архив = Новый ЧтениеФайлаАрхива(ИмяВременногоАрхива);
        
        Для Каждого ЭлементАрхива Из Архив.Элементы Цикл
            ИмяРаспакованногоФайла = ПолучитьИмяВременногоФайла("json");
            Архив.Извлечь(ЭлементАрхива, ИмяРаспакованногоФайла);
            
            // Читаем распакованный JSON
            ТекстовыйДок = Новый ТекстовыйДокумент;
            ТекстовыйДок.Прочитать(ИмяРаспакованногоФайла, КодировкаТекста.UTF8);
            СтрокаОтвета = ТекстовыйДок.ПолучитьТекст();
            
            Сообщить("Распакованный ответ: " + СтрокаОтвета);
            
            // Удаляем временный распакованный файл
            УдалитьФайлы(ИмяРаспакованногоФайла);
        КонецЦикла;
        
        Архив.Закрыть();
        УдалитьФайлы(ИмяВременногоАрхива);
    Иначе
        // Если сервер проигнорировал gzip и прислал обычный текст
        СтрокаОтвета = Ответ.ПолучитьТелоКакСтроку(КодировкаТекста.UTF8);
        Сообщить(СтрокаОтвета);
    EndЕсли;
КонецЕсли;

Решение №3: Обходные пути для устаревших версий платформы

Что делать, если ваша база данных работает на старых релизах платформы, где встроенного ЧтениеФайлаАрхива еще нет, но отключать сжатие нельзя из-за высоких требований к производительности сети? Разберем основные альтернативные подходы:

  1. Использование внешних компонент. Существуют специализированные библиотеки (Native API), написанные на C++, которые можно подключить к 1С. Они позволяют декомпрессировать gzip-потоки в памяти без создания временных файлов. Это наиболее стабильный вариант для кроссплатформенных решений (работающих как на Windows, так и на Linux).
  2. Преобразование GZIP в ZIP (Zip-хак). Механизмы сжатия в gzip и zip используют один и тот же алгоритм DEFLATE. Отличие заключается только в заголовках файлов. Программисты научились программно на уровне двоичных данных заменять заголовок gzip на стандартный заголовок zip-архива. Полученный файл затем успешно распаковывается встроенным объектом ЧтениеZipФайла.
  3. Использование COM-объектов на Windows. Если сервер 1С работает под управлением операционной системы Windows, для выполнения HTTP-запросов вместо объекта HTTPСоединение можно использовать системный COM-объект WinHttp.WinHttpRequest.5.1. Он умеет осуществлять автоматическую декомпрессию gzip-трафика на уровне операционной системы, отдавая в код 1С уже готовое текстовое тело ответа.

Резюме и рекомендации

Выбор оптимального пути решения проблемы зависит от условий разработки:

← На главную