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

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

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

Причина: семантика протокола HTTP и метод GET

Посмотрим на типовой ошибочный код отправки данных со стороны вызывающей конфигурации:


Соединение = Новый HTTPСоединение("server.domain.local", , "user", "password");
Запрос = Новый HTTPЗапрос("QueryInsurance");
Запрос.УстановитьТелоИзСтроки("11");

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

На стороне конфигурации-приемника в методе HTTP-сервиса выполняется чтение:


Функция ШаблонURLПолучитьДанныеОСтраховании(Запрос)
    УстановитьПривилегированныйРежим(Истина);
    ТелоЗапроса = Запрос.ПолучитьТелоКакСтроку(); // Возвращает пустую строку!
    ...
КонецФункции

Выясним причину такого поведения. Метод платформы Соединение.Получить() выполняет стандартный HTTP-запрос методом GET. Согласно спецификациям протокола HTTP, семантика передачи тела в запросах с методом GET не определена и не предполагает передачу полезной нагрузки (payload).

Большинство современных веб-серверов (Apache HTTP Server, IIS, Nginx), через которые публикуются базы 1С, а также промежуточные прокси-серверы при обработке метода GET просто отсекают тело сообщения и заголовок Content-Length. Модуль расширения веб-сервера 1С принимает запрос уже без содержимого, поэтому метод Запрос.ПолучитьТелоКакСтроку() возвращает пустую строку. Даже если объект HTTPЗапрос в исходной базе содержал строку, до логики конфигурации-приемника эти байты физически не доходят.

Решение 1. Переход на метод POST (ОтправитьДляОбработки)

Если для выполнения запроса требуется передать произвольное тело (строку, JSON, XML), правильным архитектурным решением является смена HTTP-метода с GET на POST. Разберем по шагам, какие изменения необходимо внести в обеих базах.

  1. Настройка метаданных в конфигурации-приемнике:

    В дереве метаданных найдем наш HTTP-сервис, раскроем нужный Шаблон URL. Если ранее там был создан только метод GET, добавим подчиненный метод POST (или заменим существующий), назначив ему имя и функцию-обработчик в модуле сервиса. Если этот шаг пропустить, вызывающая сторона при отправке данных получит ошибку 405 Method Not Allowed.

  2. Изменение кода отправки на клиенте:

    Вместо метода Соединение.Получить() вызовем метод Соединение.ОтправитьДляОбработки(). Также хорошей практикой является явное указание заголовка типа контента и параметров кодировки без использования BOM (Byte Order Mark):

    
    Соединение = Новый HTTPСоединение("server.domain.local", , "user", "password");
    
    Заголовки = Новый Соответствие;
    Заголовки.Вставить("Content-Type", "text/plain; charset=utf-8");
    
    Запрос = Новый HTTPЗапрос("QueryInsurance", Заголовки);
    Запрос.УстановитьТелоИзСтроки("11", КодировкаТекста.UTF8, ИспользованиеBOM.НеИспользовать);
    
    Ответ = Соединение.ОтправитьДляОбработки(Запрос);
    
  3. Получение данных в обработчике сервиса:

    Теперь функция-обработчик метода POST гарантированно прочитает переданную строку:

    
    Функция ШаблонURLQueryInsurancePOST(Запрос)
        УстановитьПривилегированныйРежим(Истина);
        
        ТелоСтрокой = Запрос.ПолучитьТелоКакСтроку(КодировкаТекста.UTF8);
        
        // Формируем ответ клиенту
        Ответ = Новый HTTPСервисОтвет(200);
        Ответ.Заголовки.Вставить("Content-Type", "application/json; charset=utf-8");
        Ответ.УстановитьТелоИзСтроки("{""status"": ""ok""}");
        Возврат Ответ;
    КонецФункции
    

Решение 2. Передача параметров через URL (для метода GET)

Если по условиям задачи метод запроса должен оставаться строго GET (например, операция является идемпотентной выборкой данных без изменения состояния системы), передавать информацию в теле запроса нельзя. В этом случае данные передаются через параметры адресной строки (query parameters).

Рассмотрим, как сформировать запрос с параметрами на стороне клиента:


// Передаем параметр прямо в относительный URL
ИмяПараметра = "insuranceId";
ЗначениеПараметра = "11";
ОтносительныйURL = "QueryInsurance?" + ИмяПараметра + "=" + ТекстДляURL(ЗначениеПараметра);

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

На стороне HTTP-сервиса в конфигурации 1С переданные параметры считываются через свойство ПараметрыЗапроса объекта HTTPСервисЗапрос:


Функция ШаблонURLПолучитьДанныеОСтраховании(Запрос)
    УстановитьПривилегированныйРежим(Истина);
    
    ЗначениеИД = "";
    Если Запрос.ПараметрыЗапроса.Свойство("insuranceId", ЗначениеИД) Тогда
        // Обработка полученного идентификатора "11"
    КонецЕсли;
    
    Ответ = Новый HTTPСервисОтвет(200);
    Возврат Ответ;
КонецФункции

Решение 3. Передача данных через заголовки запроса

Если объем передаваемых данных невелик (токены, простые числовые коды, идентификаторы сессий), а URL перегружать параметрами нежелательно, можно передать данные в заголовках HTTP-пакета. Заголовки сохраняются веб-сервером как при GET, так и при любых других методах.

Пример отправки через заголовки:


Заголовки = Новый Соответствие;
Заголовки.Вставить("X-Insurance-ID", "11");

Запрос = Новый HTTPЗапрос("QueryInsurance", Заголовки);
Ответ = Соединение.Получить(Запрос);

Чтение заголовка на стороне сервиса:


Функция ШаблонURLПолучитьДанныеОСтраховании(Запрос)
    ЗначениеИД = Запрос.Заголовки.Получить("X-Insurance-ID");
    // Обработка данных
    Возврат Новый HTTPСервисОтвет(200);
КонецФункции

Диагностика проблем при передаче тела запроса

Если вы уже перешли на метод POST, но тело всё равно оказывается пустым, разберем ключевые шаги для проверки:

  1. Проверка сырых двоичных данных: вызовите метод Запрос.ПолучитьТелоКакДвоичныеДанные(). Если метод возвращает объект размером 0 байт, тело было потеряно веб-сервером до входа в платформу. Если размер больше нуля, проблема кроется в несовпадении кодировок при вызове ПолучитьТелоКакСтроку().
  2. Повторное чтение потока: обратите внимание, не вызывается ли метод Запрос.ПолучитьТелоКакПоток() в общих модулях логирования или подсистемах аудита до вашего кода. Поток может быть уже прочитан до конца, из-за чего повторная попытка вернет пустоту.
  3. Инициализация HTTPСоединение: первый параметр конструктора Новый HTTPСоединение("host") должен содержать только имя сервера (хост) и порт без указания протоколов (http:// или https://) и без путей публикации. Пути публикации указываются в конструкторе HTTPЗапрос.
← На главную