При интеграции 1С со сторонними веб-сервисами часто возникает задача скачивания файлов, например, электронных счетов-фактур в формате XML. На первый взгляд, процесс кажется простым: отправить запрос и получить ответ. Однако на практике разработчики сталкиваются с рядом проблем: тело ответа оказывается пустым, возвращается непонятный поток данных или сервер отвечает кодом 302. Давайте подробно разберем по шагам, как правильно настроить HTTP-запрос, обработать ответ и прочитать полученный XML-файл.
Рассмотрим классическую ситуацию: мы обращаемся к HTTP-сервису, который должен вернуть файл XML. Мы используем объект HTTPСоединение, формируем HTTPЗапрос и выполняем метод Получить. Пытаемся извлечь тело ответа с помощью метода ПолучитьТелоКакСтроку, но получаем пустую строку. Если мы используем метод ПолучитьТелоКакПоток, платформа возвращает объект типа Поток, с которым непонятно, что делать дальше.
Проанализируем ситуацию: если метод возврата строки дает пустой результат, то, скорее всего, тело ответа действительно пустое. Чтение его как потока в данном случае не поможет. Выясним причину такого поведения веб-сервера.
Многие современные API требуют обязательной передачи логина и пароля. Если передать эти параметры в строке адреса (GET-запрос) вида get_file.php?login=xxx&pass=xxx, сервер может отклонить запрос из соображений безопасности (так как URL-адреса часто логируются прокси-серверами) или просто не понять его.
Разберем решение этой проблемы. Нам необходимо использовать POST-запрос, передавая параметры авторизации в теле запроса, а не в URL. При этом важно указать правильный заголовок, сообщающий серверу формат передаваемых данных.
Посмотрим на пример рабочего кода, решающего данную задачу:
Сервер = "api.example.ru";
Адрес = "get_invoice.php";
// Создаем защищенное соединение
Соединение = Новый HTTPСоединение(Сервер,,,,,180, Новый ЗащищенноеСоединениеOpenSSL(Неопределено, Неопределено));
// Устанавливаем заголовки
Заголовки = Новый Соответствие;
Заголовки.Вставить("Content-Type", "application/x-www-form-urlencoded");
HTTPЗапрос = Новый HTTPЗапрос(Адрес, Заголовки);
// Передаем параметры авторизации в теле запроса
HTTPЗапрос.УстановитьТелоИзСтроки("&login=ВашЛогин&pass=ВашПароль");
// Выполняем POST-запрос
HTTPОтвет = Соединение.ОтправитьДляОбработки(HTTPЗапрос);
// Получаем текст ответа
ТекстОтвета = HTTPОтвет.ПолучитьТелоКакСтроку();
Используя метод ОтправитьДляОбработки и заголовок Content-Type: application/x-www-form-urlencoded, мы корректно передаем данные на сервер и получаем содержимое нашего XML-файла в виде строки.
Что делать, если мы все равно получаем пустой ответ? Проанализируем код состояния, который возвращает сервер в свойстве HTTPОтвет.КодСостояния. Если он равен 302 (Found), это означает, что сервер выполнил перенаправление.
В 1С объект HTTPСоединение не переходит по редиректам автоматически. Сервер говорит нам: "Документ не здесь, он по другому адресу", и этот новый адрес передается в заголовках ответа.
Разберем по шагам, как обработать такую ситуацию:
HTTPОтвет.Заголовки и ищем заголовок Location.index.php?file_id=123).HTTPЗапрос с этим адресом и повторяем получение данных.Если API возвращает очень большой XML-файл, считывать его целиком в память с помощью ПолучитьТелоКакСтроку нерационально, это может вызвать переполнение памяти. В таких случаях мы как раз и используем ПолучитьТелоКакПоток.
Рассмотрим, как прочитать полученный поток. В 1С объект ЧтениеXML умеет работать с потоками напрямую, без предварительного сохранения в файл или строку.
ПотокОтвета = HTTPОтвет.ПолучитьТелоКакПоток();
ЧтениеXML = Новый ЧтениеXML;
ЧтениеXML.ОткрытьПоток(ПотокОтвета);
Пока ЧтениеXML.Прочитать() Цикл
// Обработка узлов XML
Если ЧтениеXML.ТипУзла = ТипУзлаXML.НачалоЭлемента Тогда
ИмяУзла = ЧтениеXML.Имя;
// Дальнейшая логика разбора
КонецЕсли;
КонецЦикла;
ЧтениеXML.Закрыть();
Для работы с бинарными или произвольными потоками данных (начиная с платформы 8.3.9) также можно использовать удобный объект ЧтениеДанных, который позволяет вычитывать информацию по частям.
Если вам требуется не просто прочитать данные, а сохранить XML-файл на диск пользователя, у объекта HTTPСоединение в методе Получить есть второй параметр — путь к файлу.
Кроме того, если вы обращали внимание на работу браузера, при скачивании файла ему автоматически присваивается правильное имя. Сервер передает это имя в специальном заголовке Content-Disposition. Разберем, как мы можем это использовать:
// Получаем заголовок
ЗаголовокИмени = HTTPОтвет.Заголовки.Получить("Content-Disposition");
// Заголовок обычно имеет вид: attachment; filename="invoice_123.xml"
// Парсим строку, чтобы достать имя файла, и сохраняем
ИмяФайла = ИзвлечьИмяФайлаИзЗаголовка(ЗаголовокИмени); // Ваша кастомная функция парсинга
ПутьКФайлу = КаталогВременныхФайлов() + ИмяФайла;
// Сохраняем тело ответа в файл
HTTPОтвет.ЗаписатьТелоВФайл(ПутьКФайлу);
Важный момент: Если вы столкнулись со сложностями при отладке (например, не знаете, какие именно заголовки возвращает сервер), используйте объект ЗаписьЖурналаРегистрации. Записывайте в журнал в цикле все ключи и значения из коллекции HTTPОтвет.Заголовки. Это поможет вам увидеть реальный Content-Type, наличие Content-Encoding: gzip (сжатие) и другие параметры, скрытые от глаз без применения сторонних программ-снифферов.