При интеграции 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С не умеет работать со сжатыми данными. Для этого используется 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-данных, скорость их получения может существенно снизиться.
Если вы работаете на современной версии платформы 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Если;
КонецЕсли;
Что делать, если ваша база данных работает на старых релизах платформы, где встроенного ЧтениеФайлаАрхива еще нет, но отключать сжатие нельзя из-за высоких требований к производительности сети? Разберем основные альтернативные подходы:
ЧтениеZipФайла.HTTPСоединение можно использовать системный COM-объект WinHttp.WinHttpRequest.5.1. Он умеет осуществлять автоматическую декомпрессию gzip-трафика на уровне операционной системы, отдавая в код 1С уже готовое текстовое тело ответа.Выбор оптимального пути решения проблемы зависит от условий разработки:
Accept-Encoding: identity.ЧтениеФайлаАрхива для нативной и быстрой распаковки gzip прямо на сервере 1С.