Как правильно принять и прочитать HTTP-запрос с файлами и JSON из Postman в 1С?

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

При разработке интеграций часто возникает задача передать в 1С не просто текстовое сообщение, а сложный пакет данных, содержащий одновременно структуру JSON и один или несколько файлов (изображения, документы). Стандартным способом для такой передачи является формат multipart/form-data. Однако при тестировании через популярный инструмент Postman разработчики часто сталкиваются с тем, что код, написанный по примерам из ИТС, отказывается корректно читать тело запроса. Разберем эту ситуацию подробно, выясним причины ошибок и реализуем надежный механизм парсинга.

Анализ структуры multipart-запроса

Для начала проанализируем, как выглядит «сырое» тело запроса, которое приходит в наш HTTP-сервис. В формате multipart/form-data всё тело разделено на логические блоки специальной строкой-разделителем (boundary). Рассмотрим пример типичного запроса, отправленного из Postman:


----------------------------959821427642407330468393
Content-Disposition: form-data; name="improvementRequest"
Content-Type: application/json
 {
"author": { "phoneNumber": "+79991569033", "firstName": "Иван" },
"organisation": "ТК «СОЛНЕЧНЫЙ ДАР»"
}
----------------------------959821427642407330468393
Content-Disposition: form-data; name="files"; filename="image.jpg"
Content-Type: image/jpeg

[Двоичные данные файла]
----------------------------959821427642407330468393--

Важно обратить внимание на разделитель. Согласно спецификациям RFC 2046 и RFC 7578, в заголовке Content-Type указывается базовый разделитель, но внутри тела перед каждым блоком к нему добавляются два дефиса --, а в самом конце — два дефиса в начале и в конце. Проблема многих примеров в том, что они ожидают разделитель в формате ==разделитель, в то время как Postman и большинство современных систем используют стандартные --.

Настройка Postman для корректной отправки

Прежде чем приступать к коду в 1С, проанализируем настройки на стороне клиента. Тестирование HTTP-сервисов показывает, что одной из критических ошибок является ручное указание заголовка Content-Type со значением multipart/form-data во вкладке Headers.

Важное правило: никогда не прописывайте этот заголовок вручную в Postman. Если вы его укажете сами, Postman не сможет динамически сгенерировать уникальный boundary, и сервер 1С не поймет, где начинаются и заканчиваются части данных. Правильно будет выбрать во вкладке Body тип form-data, и тогда Postman сам сформирует нужные заголовки при отправке.

Для детальной отладки рекомендуется использовать Postman Console (сочетание клавиш Alt + Ctrl + C). Там можно увидеть реальное содержимое тела запроса со всеми служебными символами, что позволит сразу выявить проблему несоответствия разделителей, а также использовать инструменты просмотра JSON.

Реализация парсинга в 1С

Теперь перейдем к реализации серверной части. Основная задача — получить тело запроса как ДвоичныеДанные, найти в них маркеры-разделители и разрезать общий буфер на части. Рассмотрим обновленный алгоритм, который учитывает особенности формирования разделителей в Postman.

Создадим функцию ПрочитатьСообщение, которая будет принимать двоичные данные тела запроса и строку разделителя (ее можно достать из заголовков запроса).


&НаСервере
Функция ПрочитатьСообщение(Тело, Разделитель)
    
    // Подготавливаем массив возможных маркеров для разделения данных
    // Разные клиенты могут добавлять разные символы перевода строки (CRLF)
    Маркеры = Новый Массив();
    // Стандартные маркеры с дефисами (для Postman)
    Маркеры.Добавить(ПолучитьБуферДвоичныхДанныхИзСтроки("--" + Разделитель));
    Маркеры.Добавить(ПолучитьБуферДвоичныхДанныхИзСтроки("--" + Разделитель + Символы.ПС));
    Маркеры.Добавить(ПолучитьБуферДвоичныхДанныхИзСтроки("--" + Разделитель + Символы.ВК));
    Маркеры.Добавить(ПолучитьБуферДвоичныхДанныхИзСтроки("--" + Разделитель + Символы.ВК + Символы.ПС));
    Маркеры.Добавить(ПолучитьБуферДвоичныхДанныхИзСтроки("--" + Разделитель + "--"));
    
    // Дополнительные маркеры (для совместимости с другими системами)
    Маркеры.Добавить(ПолучитьБуферДвоичныхДанныхИзСтроки("==" + Разделитель));
    
    ТаблицаФайлов = Новый ТаблицаЗначений;
    ТаблицаФайлов.Колонки.Добавить("ДвоичныеДанные");
    ТаблицаФайлов.Колонки.Добавить("ИмяФайла");
    
    // Используем ЧтениеДанных для работы с потоком двоичных данных
    ЧтениеДанных = Новый ЧтениеДанных(Тело);
    
    // Пропускаем первый разделитель в начале потока
    ЧтениеДанных.ПропуститьДо(Маркеры);
    
    ОбщийБуфер = ЧтениеДанных.ПрочитатьВБуферДвоичныхДанных();
    // Разделяем общий буфер на массив буферов по маркерам
    БуферыЧастей = ОбщийБуфер.Разделить(Маркеры);
    
    Результат = Новый Структура("ДанныеJSON, Файлы", Неопределено, ТаблицаФайлов);
    
    Для Каждого БуферИзЧасти Из БуферыЧастей Цикл
        
        ПотокЧасти = Новый ПотокВПамяти(БуферИзЧасти);
        ЧтениеЧасти = Новый ЧтениеДанных(ПотокЧасти);
        
        // Сначала читаем заголовки конкретной части (имя поля, тип контента)
        ЗаголовкиЧасти = ПрочитатьЗаголовкиБлока(ЧтениеЧасти);
        СведенияОКонтенте = РазведатьContentDisposition(ЗаголовкиЧасти);
        
        Если СведенияОКонтенте.name = "improvementRequest" Тогда
            // Если это текстовое поле с JSON, читаем его как строку
            СтрокаJSON = ЧтениеЧасти.ПрочитатьСимволы();
            Результат.ДанныеJSON = СтрокаJSON;
        ИначеЕсли ЗначениеЗаполнено(СведенияОКонтенте.filename) Тогда
            // Если в заголовке указано имя файла - сохраняем как двоичные данные
            НовыйФайл = ТаблицаФайлов.Добавить();
            НовыйФайл.ДвоичныеДанные = ЧтениеЧасти.Прочитать().ПолучитьДвоичныеДанные();
            НовыйФайл.ИмяФайла = СведенияОКонтенте.filename;
        КонецЕсли;
        
        ЧтениеЧасти.Закрыть();
    КонецЦикла;
    
    Возврат Результат;
КонецФункции

Обработка заголовков каждой части

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


Функция ПрочитатьЗаголовкиБлока(ЧтениеЧасти)
    Заголовки = Новый Соответствие();
    Пока Истина Цикл
        Стр = ЧтениеЧасти.ПрочитатьСтроку();
        Если Стр = "" Тогда // Пустая строка означает окончание секции заголовков
            Прервать;
        КонецЕсли;
        
        ПозицияРазделителя = СтрНайти(Стр, ":");
        Если ПозицияРазделителя > 0 Тогда
            Имя = СокрЛП(Лев(Стр, ПозицияРазделителя - 1));
            Значение = СокрЛП(Сред(Стр, ПозицияРазделителя + 1));
            Заголовки.Вставить(Имя, Значение);
        КонецЕсли;
    КонецЦикла;
    Возврат Заголовки;
КонецФункции

Рассмотрим подробнее заголовок Content-Disposition. Именно в нем передаются имена полей формы (параметр name) и оригинальные имена файлов (параметр filename). Важно учитывать, что если имя файла содержит кириллицу, Postman может передать его в кодированном виде (RFC 5987), например: filename*=UTF-8''%D0%A3.jpg. Для полноценного решения потребуется декодирование таких строк, однако в базовом варианте достаточно извлечь текст между кавычками.

Почему использование Base64 не всегда оправдано?

Часто в комментариях советуют просто кодировать файлы в Base64 и передавать их внутри обычного JSON. Разберем, почему способ multipart/form-data, который мы реализовали выше, лучше:

  1. Объем данных: Кодирование в Base64 увеличивает размер файла примерно на 33%. При передаче больших изображений или тяжелых документов это создает лишнюю нагрузку на сеть и память сервера.
  2. Производительность: Объекты ЧтениеДанных и БуферДвоичныхДанных в 1С позволяют работать с файлами максимально эффективно на низком уровне.
  3. Стандарты: Multipart является стандартом де-факто для загрузки файлов в веб-технологиях и интеграции 1С с внешними системами.

Рекомендации по отладке

Проанализируем типичные причины, по которым файлы могут приходить «битыми»:

Таким образом, мы выяснили, что ключ к успешной отладке запросов из Postman кроется в правильной обработке разделителей блоков и внимательном чтении заголовков каждой части multipart сообщения. Использование БуферДвоичныхДанных делает этот процесс быстрым и надежным.

← На главную