Как извлечь и прочитать JSON из тела HTTP-запроса в формате form-urlencoded в 1С?

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

При интеграции мобильных приложений и сторонних веб-сервисов с базой 1С через модуль HTTPСервис часто возникает ситуация, когда данные передаются не в виде чистого JSON-документа в теле запроса (application/json), а упаковываются в виде классических полей формы (application/x-www-form-urlencoded). В этом случае в теле запроса приходит строка вида report_data={...JSON...}&device_id=123, где параметры могут следовать в произвольном порядке, а значения закодированы спецсимволами URL-encoding.

Разберем типичные ошибки, выясним причину сбоев при попытке сразу декодировать строку и построим универсальный алгоритм для корректного разбора параметров и десериализации JSON.

Почему возникает ошибка при попытке сразу декодировать всю строку?

Проанализируем распространенную ошибку: если применить функцию РаскодироватьСтроку() сразу ко всему телу запроса, полученному через Запрос.ПолучитьТелоКакСтроку(), то все закодированные разделители (например, символы =, & или фигурные скобки внутри строковых полей JSON) декодируются одновременно. В результате строка превращается в смесь служебных разделителей формы и содержимого вложенного JSON. Попытка разделить такую строку на части по символам & или = приведет к искажению данных, а передача всей строки в ПрочитатьJSON() вызовет исключение и аварийное завершение работы сервиса с кодом 500 Internal Server Error.

Также важно помнить, что встроенное соответствие ПараметрыЗапроса объекта HTTPСервисЗапрос заполняется платформой только для параметров, переданных в строке URL (URI Query String). Если же данные отправлены методом POST или PUT в теле запроса, платформа 1С не разбирает тело автоматически — парсинг необходимо выполнять в коде сервиса.

Правильный порядок обработки входящего запроса

Разберем по шагам строгую последовательность действий, соответствующую стандартам RFC:

  1. Получение сырого тела запроса — считываем входящие данные как строку без предварительного декодирования.
  2. Разбиение строки на пары параметров — разделяем строку по символу &. Так как значения параметров внутри тела еще закодированы (URL-encoded), служебные амперсанды внутри значений не нарушат разделение.
  3. Разделение ключа и сырого значения — в каждой паре находим позицию первого знака равенства (=). Все, что слева от него — имя параметра, а все, что справа — его закодированное значение.
  4. Декодирование значения — применяем функцию РаскодироватьСтроку() с параметром СпособКодированияСтроки.КодировкаURL только к значению конкретного параметра.
  5. Обработка специфики URL-кодирования — по стандарту пробелы в формах часто заменяются знаком +. Рекомендуется заменить + на стандартный пробел перед дальнейшей обработкой.
  6. Десериализация JSON — передаем декодированную строку нужного параметра в объект ЧтениеJSON и десериализуем через ПрочитатьJSON().

Реализация универсальной функции разбора параметров тела запроса

Посмотрим на пример универсальной функции, которая преобразует URL-encoded тело запроса в удобную Структура или Соответствие:


Функция РазобратьПараметрыТела(Знач ТелоСтрока) Экспорт

    Результат = Новый Структура;
    
    Если ПустаяСтрока(ТелоСтрока) Тогда
        Возврат Результат;
    КонецЕсли;
    
    // Разбиваем тело на отдельные параметры по знаку &
    МассивПараметров = СтрРазделить(ТелоСтрока, "&", Ложь);
    
    Для Каждого СтрПараметр Из МассивПараметров Цикл
        
        ПозицияРавно = СтрНайти(СтрПараметр, "=");
        
        Если ПозицияРавно = 0 Тогда
            // Параметр без значения или флаговый параметр
            ИмяПараметра = СтрПараметр;
            ЗначениеСырое = "";
        Иначе
            ИмяПараметра = Лев(СтрПараметр, ПозицияРавно - 1);
            ЗначениеСырое = Сред(СтрПараметр, ПозицияРавно + 1);
        КонецЕсли;
        
        // Заменяем знак '+' на пробел перед URL-декодированием
        ЗначениеСырое = СтрЗаменить(ЗначениеСырое, "+", " ");
        
        // Раскодируем только значение параметра
        ЗначениеПараметра = РаскодироватьСтроку(ЗначениеСырое, СпособКодированияСтроки.КодировкаURL);
        
        // Сохраняем параметр в структуру
        Результат.Вставить(ИмяПараметра, ЗначениеПараметра);
        
    КонецЦикла;
    
    Возврат Результат;

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

Чтение JSON и безопасная обработка в методе HTTP-сервиса

Теперь рассмотрим, как применить созданную функцию непосредственно в методе-обработчике HTTP-сервиса. Важно обернуть чтение JSON в блок Попытка ... Исключение, чтобы в случае некорректного синтаксиса возвращать клиенту статус 400 Bad Request с понятным описанием, а не падать с кодом 500.


Функция ОтчетыPOST(Запрос)
    
    // 1. Считываем тело запроса
    ТелоСтрока = Запрос.ПолучитьТелоКакСтроку();
    
    // 2. Разбираем параметры формы
    ПараметрыТела = РазобратьПараметрыТела(ТелоСтрока);
    
    // Проверяем наличие обязательного параметра с JSON-данными
    Если НЕ ПараметрыТела.Свойство("report_data") Тогда
        Ответ = Новый HTTPСервисОтвет(400);
        Ответ.УстановитьТелоИзСтроки("В теле запроса отсутствует обязательный параметр 'report_data'");
        Возврат Ответ;
    КонецЕсли;
    
    СтрокаJSON = ПараметрыТела.report_data;
    
    // 3. Десериализуем JSON
    Чтение = Новый ЧтениеJSON;
    Попытка
        Чтение.УстановитьСтроку(СтрокаJSON);
        ДанныеОтчета = ПрочитатьJSON(Чтение);
        Чтение.Закрыть();
    Исключение
        Чтение.Закрыть();
        Ответ = Новый HTTPСервисОтвет(400);
        Ответ.УстановитьТелоИзСтроки("Ошибка парсинга JSON: " + ОписаниеОшибки());
        Возврат Ответ;
    КонецПопытки;
    
    // 4. Получаем дополнительные параметры (если они переданы)
    ИдентификаторУстройства = "";
    Если ПараметрыТела.Свойство("device_id") Тогда
        ИдентификаторУстройства = ПараметрыТела.device_id;
    КонецЕсли;
    
    // 5. Выполняем прикладную обработку полученных данных
    // Например: ОбработатьДанныеОтчета(ДанныеОтчета, ИдентификаторУстройства);
    
    // 6. Формируем успешный ответ клиенту
    Ответ = Новый HTTPСервисОтвет(200);
    Ответ.Заголовки.Вставить("Content-Type", "application/json; charset=utf-8");
    Ответ.УстановитьТелоИзСтроки("{""status"": ""success""}");
    Возврат Ответ;

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

Полезные практические рекомендации

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

  1. Кодировка входящих данных: метод Запрос.ПолучитьТелоКакСтроку() по умолчанию считывает данные в кодировке UTF-8. Если внешний источник отправляет данные в другой кодировке (например, Windows-1251), укажите её явным параметром при вызове метода.
  2. Потоковое чтение больших объемов: если вложенный JSON имеет огромный размер и загрузка всего документа в память через ПрочитатьJSON() нежелательна, можно использовать пошаговый обход узлов через методы Чтение.Прочитать() и проверку Чтение.ТипУзла.
  3. Универсальность структуры: использование функции разбора параметров гарантирует, что изменение порядка полей (например, если device_id придет раньше report_data) или появление новых параметров не сломает работу HTTP-сервиса.
← На главную