Как исправить ошибки кодировки и спецсимволов при отправке JSON запросов в 1С

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

При интеграции 1С с внешними API (например, сервисами «Честный ЗНАК» (поможет модуль автоматизации интеграции с системой Честный ЗНАК по API) и его разрешительным режимом, логистическими компаниями или маркетплейсами) разработчики часто сталкиваются с ошибками вида Invalid UTF-8 start byte или Invalid symbol '\r'. Эти ошибки возникают на стороне сервера из-за некорректного формирования тела HTTP-запроса. В данной статье мы подробно разберем, как правильно подготовить данные, избежать проблем с кодировкой и корректно упаковать вложенные структуры в формат Base64.

Разбор типичной ошибки: Структура вместо Строки

Проанализируем первую распространенную ошибку. Часто начинающие разработчики пытаются передать объект типа Структура напрямую в метод УстановитьТелоИзСтроки. Рассмотрим, почему так делать нельзя.

Метод УстановитьТелоИзСтроки объекта HTTPЗапрос ожидает в качестве первого параметра именно текстовую строку. Если вы передадите туда Структура, 1С не выполнит автоматическую конвертацию в JSON. Вместо этого сервер получит либо пустые данные, либо ошибку приведения типов еще на стороне клиента. Выясним, как выглядит правильный алгоритм:

  1. Сначала создаем структуру данных.
  2. Сериализуем структуру в строку формата JSON с помощью объекта ЗаписьJSON.
  3. Передаем полученную строку в тело запроса.

Посмотрим на пример правильной подготовки тела запроса:


// 1. Формируем структуру
ДанныеЗапроса = Новый Структура;
ДанныеЗапроса.Вставить("documentBody", ЗашифрованныеДанные);

// 2. Сериализуем в JSON
ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.УстановитьСтроку();
ЗаписатьJSON(ЗаписьJSON, ДанныеЗапроса);
СтрокаТела = ЗаписьJSON.Закрыть();

// 3. Устанавливаем тело запроса
HTTPЗапрос.УстановитьТелоИзСтроки(СтрокаТела, КодировкаТекста.UTF8, ИспользованиеByteOrderMark.НеИспользовать);

Проблема символов переноса строк в Base64

Одной из самых коварных проблем является ошибка Invalid symbol '\r' (или символ с кодом 15/13). Разберем причину ее появления. Стандартная функция 1С Base64Строка() работает по спецификации MIME, которая требует вставлять символы переноса строки (CRLF) через каждые 72 символа. Однако большинство современных веб-сервисов ожидают Base64 в виде одной непрерывной строки.

Если в вашем JSON-поле documentBody окажутся символы \r (возврат каретки) или \n (перенос строки), сервер вернет ошибку 400 (Bad Request), так как парсер JSON на стороне сервера споткнется о неэкранированные спецсимволы внутри строки. Проанализируем, как очистить строку перед отправкой:


// Получаем двоичные данные из строки вложенного документа
ДвоичныеДанныеВложения = ПолучитьДвоичныеДанныеИзСтроки(ВложенныйJSONСтрокой, КодировкаТекста.UTF8, Ложь);

// Кодируем в Base64
СтрокаBase64 = Base64Строка(ДвоичныеДанныеВложения);

// КРИТИЧЕСКИ ВАЖНО: Удаляем все символы переноса строк
СтрокаBase64 = СтрЗаменить(СтрокаBase64, Символы.ПС, "");
СтрокаBase64 = СтрЗаменить(СтрокаBase64, Символы.ВК, "");

Важный момент: использование метода СтрЗаменить гарантирует, что итоговый JSON будет валидным и принят сервером без ошибок парсинга.

Ошибки кодировки и Byte Order Mark (BOM)

Рассмотрим ситуацию с ошибкой Invalid UTF-8 start byte 0xa1. Эта ошибка сигнализирует о том, что сервер получил байты, которые не соответствуют стандарту UTF-8. В контексте 1С это чаще всего происходит по двум причинам:

  1. Использование BOM: 1С может добавлять в начало строки невидимые символы (метку порядка байтов). Многие API воспринимают это как мусор в начале запроса. Всегда используйте ИспользованиеByteOrderMark.НеИспользовать.
  2. Неявная кодировка при работе с файлами: Если вы сначала записываете JSON во временный файл через ЗаписьJSON.ОткрытьФайл(), а затем читаете его как ДвоичныеДанные, 1С может использовать кодировку Windows-1251 или добавить тот же BOM.

Для решения этой проблемы, особенно если требуется надежная интеграция 1С с Google Таблицами через сервисный аккаунт, рекомендуется избегать промежуточной записи в файл и работать напрямую с памятью (строками или потоками). Посмотрим на реализацию надежного метода формирования вложенного зашифрованного документа:


НастройкиСериализации = Новый НастройкиСериализацииJSON;
НастройкиСериализации.ВариантЗаписиДаты = ВариантЗаписиДатыJSON.УниверсальнаяДата;
НастройкиСериализации.ФорматСериализацииДаты = ФорматДатыJSON.ISO;

ЗаписьJSON = Новый ЗаписьJSON;
// Записываем сразу в строку, чтобы избежать влияния файловой системы
ЗаписьJSON.УстановитьСтроку();
ЗаписатьJSON(ЗаписьJSON, ВнутренняяСтруктураДокумента, НастройкиСериализации);
ВнутреннийJSON = ЗаписьJSON.Закрыть();

// Преобразуем в Base64 без лишних промежуточных звеньев
ТелоBase64 = Base64Строка(ПолучитьДвоичныеДанныеИзСтроки(ВнутреннийJSON, КодировкаТекста.UTF8, Ложь));
ТелоBase64 = СтрЗаменить(СтрЗаменить(ТелоBase64, Символы.ПС, ""), Символы.ВК, "");

Финальная сборка HTTP-запроса

Теперь объединим все знания в единый блок кода, который обеспечит корректную отправку данных. Мы проанализируем настройку заголовков, создание защищенного соединения и безопасную передачу тела запроса.


// 1. Подготовка заголовков
Заголовки = Новый Соответствие;
Заголовки.Вставить("Content-Type", "application/json; charset=utf-8");
// Если API требует авторизации, например, как при получении токена RuStore с RSA-подписью
Заголовки.Вставить("Authorization", "Bearer " + ТокенДоступа);

// 2. Формирование финальной структуры JSON
ФинальнаяСтруктура = Новый Структура;
ФинальнаяСтруктура.Вставить("documentBody", ТелоBase64);

ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.УстановитьСтроку();
ЗаписатьJSON(ЗаписьJSON, ФинальнаяСтруктура);
СтрокаЗапроса = ЗаписьJSON.Закрыть();

// 3. Отправка через HTTPСоединение
Попытка
    SSL = Новый ЗащищенноеСоединениеOpenSSL();
    Соединение = Новый HTTPСоединение(ХостСервиса, 443, , , , 30, SSL);
    
    Запрос = Новый HTTPЗапрос(ПутьКМетодуAPI, Заголовки);
    Запрос.УстановитьТелоИзСтроки(СтрокаЗапроса, КодировкаТекста.UTF8, ИспользованиеByteOrderMark.НеИспользовать);
    
    Ответ = Соединение.ОтправитьДляОбработки(Запрос);
    
    Если Ответ.КодСостояния = 200 Тогда
        // Обработка успешного ответа
        Сообщить("Данные успешно отправлены!");
    Иначе
        // Анализируем ошибку от сервера
        Сообщить("Ошибка сервера: " + Ответ.КодСостояния);
        Сообщить(Ответ.ПолучитьТелоКакСтроку());
    КонецЕсли;
Исключение
    Сообщить("Ошибка связи: " + ОписаниеОшибки());
КонецПопытки;

Дополнительные рекомендации

Рассмотрим еще несколько важных нюансов, которые помогут избежать ошибок при интеграции:

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

← На главную