Почему в HTTP-сервисе 1С русские буквы в теле POST-запроса превращаются в знаки вопроса и как это исправить?

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

При разработке интеграций через HTTP-сервисы в платформе 1С:Предприятие разработчики нередко сталкиваются с неприятной проблемой: внешняя система отправляет корректный JSON в кодировке UTF-8, однако при попытке прочитать тело запроса с помощью метода ПолучитьТелоКакСтроку() все русские символы заменяются знаками вопроса (???). Разберем причины такого поведения и подробно рассмотрим рабочие способы решения задачи.

Выясним причину искажения кодировки

Проанализируем механизм обработки входящего запроса платформой 1С. Когда вызывается метод Запрос.ПолучитьТелоКакСтроку(КодировкаТекста.UTF8), ожидается, что платформа прочитает байты входящего потока с явным указанием кодировки UTF-8. Однако в объекте HTTPСервисЗапрос метод может вести себя иначе, чем в HTTPОтвет.

Платформа пытается автоматически определить кодировку текста на основании заголовка Content-Type, пришедшего от клиента. В этот момент возникает сразу несколько узких мест:

  1. Некорректный разбор составных заголовков. Если вызывающая сторона присылает заголовок вида Content-Type: text/plain; charset=UTF-8, веб-сервер или расширение 1С (особенно в ряде релизов ветки 8.3.9) могут некорректно разобрать параметр charset из-за регистра букв, наличия пробела или точки с запятой. В итоге параметр кодировки отбрасывается.
  2. Кодировка по умолчанию для text/plain. Согласно спецификациям HTTP, если для типа text/plain кодировка не распознана, по умолчанию применяется латинская однобайтовая кодировка ISO-8859-1 (или системный ANSI). В результате многобайтовые кириллические символы UTF-8 интерпретируются неверно и безвозвратно превращаются в вопросительные знаки.
  3. Игнорирование параметра метода. В проблемных версиях платформы метод ПолучитьТелоКакСтроку() может игнорировать переданный параметр кодировки, опираясь исключительно на результат парсинга заголовков веб-сервером.

Рассмотрим надежные практические способы обойти это ограничение и гарантированно прочитать кириллицу.

Способ 1. Чтение напрямую из потока через ЧтениеJSON (рекомендуемый способ)

Посмотрим на наиболее производительный и правильный подход, который позволяет вообще обойти преобразование тела запроса в промежуточную строку 1С. Объект HTTPСервисЗапрос предоставляет метод ПолучитьТелоКакПоток(), возвращающий необработанный поток данных. Мы можем передать этот поток напрямую в объект ЧтениеJSON, явно указав кодировку UTF-8.

Разберем по шагам реализацию данного способа в методе HTTP-сервиса:


Функция СоздатьЗаказ(Запрос)
    
    Ответ = Новый HTTPСервисОтвет(201);
    
    Поток = Запрос.ПолучитьТелоКакПоток();
    
    ЧтениеJSON = Новый ЧтениеJSON;
    ЧтениеJSON.ОткрытьПоток(Поток, "UTF-8");
    
    Данные = ПрочитатьJSON(ЧтениеJSON);
    
    ЧтениеJSON.Закрыть();
    Поток.Закрыть();
    
    // Выполняем дальнейшую обработку полученных данных
    
    Возврат Ответ;

КонецФункции

Преимущество этого метода заключается в том, что платформа не выполняет предварительного декодирования текста средствами веб-сервера. Парсер JSON работает напрямую с байтовым потоком и гарантированно декодирует русские буквы без искажений.

Способ 2. Чтение через двоичные данные

Если в коде требуется получить именно готовую строку JSON (например, для логирования входящего сообщения или дополнительной валидации регулярными выражениями), прочитаем тело запроса в виде «сырых» байтов. Это полностью исключает влияние некорректно разобранных HTTP-заголовков.

Рассмотрим вариант реализации для современных версий платформы 1С:Предприятие (начиная с релиза 8.3.10):


Функция СоздатьЗаказ(Запрос)
    
    Ответ = Новый HTTPСервисОтвет(201);
    
    ДвоичныеДанныеТела = Запрос.ПолучитьТелоКакДвоичныеДанные();
    СтрокаJSON = ПолучитьСтрокуИзДвоичныхДанных(ДвоичныеДанныеТела, КодировкаТекста.UTF8);
    
    ЧтениеJSON = Новый ЧтениеJSON;
    ЧтениеJSON.УстановитьСтроку(СтрокаJSON);
    
    Данные = ПрочитатьJSON(ЧтениеJSON);
    ЧтениеJSON.Закрыть();
    
    Возврат Ответ;

КонецФункции

Если обработка выполняется на более ранних релизах платформы (включая 8.3.9), где метод ПолучитьСтрокуИзДвоичныхДанных() еще отсутствует, используем чтение двоичных данных через временный файл:


Функция СоздатьЗаказ(Запрос)
    
    Ответ = Новый HTTPСервисОтвет(201);
    
    ДвоичныеДанныеТела = Запрос.ПолучитьТелоКакДвоичныеДанные();
    ИмяВременногоФайла = ПолучитьИмяВременногоФайла("json");
    ДвоичныеДанныеТела.Записать(ИмяВременногоФайла);
    
    ЧтениеТекста = Новый ЧтениеТекста(ИмяВременногоФайла, КодировкаТекста.UTF8);
    СтрокаJSON = ЧтениеТекста.Прочитать();
    ЧтениеТекста.Закрыть();
    
    УдалитьФайлы(ИмяВременногоФайла);
    
    ЧтениеJSON = Новый ЧтениеJSON;
    ЧтениеJSON.УстановитьСтроку(СтрокаJSON);
    Данные = ПрочитатьJSON(ЧтениеJSON);
    ЧтениеJSON.Закрыть();
    
    Возврат Ответ;

КонецФункции

Способ 3. Корректировка заголовков на стороне клиента

Проанализируем заголовки, которые передает вызывающая система. В исходной ситуации клиент передает данные со следующим заголовком:

Content-Type: text/plain; charset=UTF-8

Передача структуры данных JSON с типом содержимого text/plain является грубым нарушением стандартов REST-интеграций. Для передачи JSON-данных необходимо использовать специализированный MIME-тип:

Content-Type: application/json

Важный момент: согласно спецификации RFC 8259, для типа содержимого application/json кодировкой по умолчанию всегда является UTF-8. Дописывать параметр charset=UTF-8 к application/json не требуется. Если клиентская система изменит заголовок на чистый application/json, веб-сервер и платформа 1С распознают тело сообщения штатно.

Способ 4. Экранирование Unicode на стороне отправки

Если модификация серверного кода 1С нежелательна, а ошибки заголовков обойти не удается, проблему можно решить на стороне клиента. Большинство библиотек сериализации JSON поддерживают экранирование не-ASCII символов (например, опция экранирования Unicode-последовательностей). В этом случае все русские буквы передаются в виде escape-кодов вида \uXXXX. Такой запрос состоит исключительно из базовых символов ASCII, которые корректно воспринимаются абсолютно в любой однобайтовой или многобайтовой кодировке, а встроенная функция 1С ПрочитатьJSON() автоматически преобразует эти коды обратно в русские буквы.

← На главную