Мы сталкиваемся с задачей интеграции 1С с внешними системами, которая требует выполнения сложных HTTP-запросов. Один из распространенных сценариев — отправка файлов на сервер с использованием метода POST, формата `multipart/form-data` и дополнительной аутентификации через заголовки, а также с обязательным использованием клиентского сертификата для установки защищенного соединения HTTPS. Рассмотрим, как перевести типичный `curl`-запрос в рабочий код на платформе 1С.
Прежде чем приступить к написанию кода в 1С, давайте внимательно проанализируем исходный `curl`-запрос. Это поможет нам понять все его компоненты и требования к формированию HTTP-запроса в 1С. Пример `curl`-запроса:
curl -i -X POST \
-H "Content-Type:multipart/form-data" \
-H "X-auth-Subscriber:501" \
-H "X-Auth-Group:GROUP" \
-H "X-Auth-Username:TestUsr" \
-H "X-Auth-Password:usrte-s@34" \
-F "file=@\"./CHT_00501_UCH_03-00_20250402154720000.zip.pem.pem\";filename=\"CHT_00501_UCH_03-00_20250402154720000.zip.pem.pem\"" \
'https://your.server.com/api/upload'
Разберем ключевые элементы этого запроса:
-X POST: Это указывает на использование метода POST. В 1С мы установим свойство Метод объекта HTTPЗапрос в "POST".-H "Content-Type:multipart/form-data": Этот заголовок указывает, что тело запроса будет содержать данные, разделенные границей (boundary), и может включать файлы и другие поля формы. В 1С мы должны будем сформировать этот заголовок вручную, указав уникальную границу, например, Content-Type: multipart/form-data; boundary=----------WebKitFormBoundary1234567890.-H "X-auth-Subscriber:501", -H "X-Auth-Group:GROUP", -H "X-Auth-Username:TestUsr", -H "X-Auth-Password:usrte-s@34": Это пользовательские заголовки аутентификации. В 1С их необходимо добавить в коллекцию Заголовки объекта HTTPЗапрос.-F "file=@\"./CHT_00501_UCH_03-00_20250402154720000.zip.pem.pem\";filename=\"CHT_00501_UCH_03-00_20250402154720000.zip.pem.pem\"": Этот параметр указывает на отправку файла.
file: Имя поля формы, под которым файл будет передан на сервер.@\"./CHT_00501_UCH_03-00_20250402154720000.zip.pem.pem\": Путь к файлу на локальной машине.filename=\"CHT_00501_UCH_03-00_20250402154720000.zip.pem.pem\": Имя файла, которое будет отображаться на сервере.ДвоичныеДанные, затем закодировать его в Base64 и включить в тело multipart/form-data запроса с соответствующими заголовками Content-Disposition и Content-Type для этой части.
'https://your.server.com/api/upload': Это URL конечной точки, куда отправляется запрос. Важно всегда указывать полный и корректный URL, иначе запрос не будет отправлен. В 1С это будет адрес для объекта HTTPЗапрос.ЗащищенноеСоединениеOpenSSL.Прежде чем писать код, убедимся, что у нас есть все необходимые компоненты и данные:
Давайте построим функцию в 1С, которая будет выполнять этот HTTP-запрос.
Мы начинаем с создания экземпляра объекта HTTPЗапрос, указывая полный URL и метод запроса.
// Настройки подключения
АдресСервера = "https://your.server.com/api/upload"; // Укажите актуальный URL
ПутьКФайлу = "C:\CHT_00501_UCH_03-00_20250402154720000.zip.pem.pem"; // Укажите полный путь к файлу
ИмяФайлаДляСервера = "CHT_00501_UCH_03-00_20250402154720000.zip.pem.pem"; // Имя файла, под которым он будет на сервере
// Создание HTTP-запроса
ЗапросHTTP = Новый HTTPЗапрос(АдресСервера);
ЗапросHTTP.Метод = "POST";
Мы определяем АдресСервера как полную ссылку на ресурс, включая протокол и путь. Свойство Метод устанавливается в "POST" в соответствии с нашим `curl`-запросом.
Далее нам необходимо добавить пользовательские заголовки аутентификации, которые были в `curl`-запросе.
// Установка заголовков аутентификации
ЗапросHTTP.Заголовки.Вставить("X-auth-Subscriber", "501");
ЗапросHTTP.Заголовки.Вставить("X-Auth-Group", "GROUP");
ЗапросHTTP.Заголовки.Вставить("X-Auth-Username", "TestUsr");
ЗапросHTTP.Заголовки.Вставить("X-Auth-Password", "usrte-s@34");
Мы используем коллекцию Заголовки объекта HTTPЗапрос и метод Вставить() для добавления каждого заголовка и его значения.
Это самый сложный и критически важный шаг, поскольку 1С не имеет встроенной функции для автоматического формирования `multipart/form-data`. Мы должны создать тело запроса вручную, соблюдая спецификацию `multipart/form-data`.
1. **Генерация границы (boundary)**: Создадим уникальную строку, которая будет разделять части данных в теле запроса. Это может быть любой уникальный набор символов, но часто используют что-то вроде `----------WebKitFormBoundary` с добавлением случайных символов или уникального идентификатора.
// Подготовка multipart/form-data
Граница = "----------WebKitFormBoundary" + Строка(Новый УникальныйИдентификатор);
ЗапросHTTP.Заголовки.Вставить("Content-Type", "multipart/form-data; boundary=" + Граница);
Важно: Заголовок Content-Type для всего запроса должен содержать созданную нами границу.
2. **Чтение файла**: Прочитаем содержимое файла, который хотим отправить, в двоичные данные.
// Чтение файла
ДанныеФайла = Новый ДвоичныеДанные(ПутьКФайлу);
3. **Формирование частей тела запроса**: Каждая часть `multipart/form-data` начинается с `boundary`, за которым следуют заголовки `Content-Disposition`, `Content-Type` (для конкретной части) и затем сами данные. Завершается тело запроса двойным `--` после последней границы.
// Формирование тела запроса
ТелоЗапроса = "";
// Добавление части для файла
ТелоЗапроса = ТелоЗапроса + "--" + Граница + Символы.ПС; // Начало части с границей
ТелоЗапроса = ТелоЗапроса + "Content-Disposition: form-data; name=""file""; filename=""" + ИмяФайлаДляСервера + """" + Символы.ПС; // Заголовок Content-Disposition для файла, где "file" - это имя поля
ТелоЗапроса = ТелоЗапроса + "Content-Type: application/octet-stream" + Символы.ПС + Символы.ПС; // Content-Type для файла (бинарные данные), затем пустая строка
ТелоЗапроса = ТелоЗапроса + ДанныеФайла.ПолучитьBase64Строку() + Символы.ПС; // Данные файла, закодированные в Base64, затем перенос строки
// Если есть другие поля формы, их можно добавить аналогично
// Например, для текстового поля "description":
// ТелоЗапроса = ТелоЗапроса + "--" + Граница + Символы.ПС;
// ТелоЗапроса = ТелоЗапроса + "Content-Disposition: form-data; name=""description""" + Символы.ПС + Символы.ПС;
// ТелоЗапроса = ТелоЗапроса + "Это описание файла." + Символы.ПС;
// Завершающая граница
ТелоЗапроса = ТелоЗапроса + "--" + Граница + "--" + Символы.ПС;
// Установка сформированного тела запроса
ЗапросHTTP.УстановитьТелоИзСтроки(ТелоЗапроса, "text/plain; charset=UTF-8");
Здесь мы:
* Добавляем `Content-Disposition` для файла, указывая `name` (имя поля формы, `file` в нашем случае) и `filename` (имя файла для сервера).
* Указываем `Content-Type: application/octet-stream` для бинарных данных файла.
* Конвертируем ДвоичныеДанные файла в строку Base64 с помощью ПолучитьBase64Строку().
* Завершаем тело запроса последней границей с двумя дефисами (`--`) в конце.
* Устанавливаем сформированную строку в качестве тела запроса с кодировкой UTF-8.
Для установки защищенного соединения HTTPS, особенно когда требуется клиентский сертификат, недостаточно просто передать структуру с сертификатом в параметры СоединениеHTTP. Мы должны использовать более специализированные объекты 1С: ЗащищенноеСоединениеOpenSSL и HTTPСоединение.
1. **Загрузка сертификата**: Предположим, что наш сертификат находится в файле. Нам нужна функция, которая загрузит его.
// Функция для получения сертификата из файла (пример)
Функция ПолучитьСертификатИзФайла(ПутьКСертификату, ПарольСертификата = "")
Если Не СтрДлина(ПутьКСертификату) > 0 Тогда
Сообщить("Не указан путь к файлу сертификата.");
Возврат Неопределено;
КонецЕсли;
Попытка
// В зависимости от формата сертификата (PEM, PFX и т.д.)
// Используем подходящий метод загрузки.
// Для PFX файла с паролем:
Если ПарольСертификата = "" Тогда
Сертификат = Новый СертификатOpenSSL(ПутьКСертификату); // Для PEM без пароля или PFX без пароля
Иначе
Сертификат = Новый СертификатOpenSSL(ПутьКСертификату, ПарольСертификата); // Для PFX с паролем
КонецЕсли;
Возврат Сертификат;
Исключение
Сообщить("Ошибка загрузки сертификата: " + ОписаниеОшибки());
Возврат Неопределено;
КонецПопытки;
КонецФункции
Например, если у вас сертификат в формате `PEM` без пароля, вы можете использовать `Новый СертификатOpenSSL(ПутьКСертификату)`. Если это `PFX` файл с паролем, то `Новый СертификатOpenSSL(ПутьКСертификату, ПарольСертификата)`.
2. **Создание защищенного соединения**: Мы создаем объект ЗащищенноеСоединениеOpenSSL, передаем ему наш сертификат, а затем используем это защищенное соединение при создании HTTPСоединения.
// Настройка HTTPS с клиентским сертификатом
ПутьКСертификату = "C:\my_certificate.pem"; // Укажите актуальный путь к вашему клиентскому сертификату
ПарольСертификата = ""; // Если сертификат защищен паролем, укажите его
КлиентскийСертификат = ПолучитьСертификатИзФайла(ПутьКСертификату, ПарольСертификата);
Если КлиентскийСертификат = Неопределено Тогда
Сообщить("Не удалось получить клиентский сертификат. Отправка запроса невозможна.");
Возврат Неопределено;
КонецЕсли;
ПараметрыЗащищенногоСоединения = Новый Структура;
ПараметрыЗащищенногоСоединения.Вставить("ПроверятьСервер", Истина); // Рекомендуется проверять сертификат сервера
ПараметрыЗащищенногоСоединения.Вставить("КлиентскийСертификат", КлиентскийСертификат); // Передаем наш клиентский сертификат
ЗащищенноеСоединение = Новый ЗащищенноеСоединениеOpenSSL(ПараметрыЗащищенногоСоединения);
// Создание и настройка HTTP-соединения
// АдресСервера должен быть "https://your.server.com" без пути
// Путь запроса будет задаваться в HTTPЗапросе
АдресХоста = URI(АдресСервера).Хост; // Получаем только хост из полного URL
ПортХоста = URI(АдресСервера).Порт; // Получаем порт из полного URL (обычно 443 для HTTPS)
СоединениеHTTP = Новый HTTPСоединение(АдресХоста, ПортХоста, , , , , ЗащищенноеСоединение);
Здесь URI(АдресСервера).Хост используется для извлечения имени хоста из полного URL, а ЗащищенноеСоединение передается как последний параметр при создании HTTPСоединения.
Параметр ПроверятьСервер рекомендуется установить в Истина для обеспечения безопасности.
После того как все компоненты запроса сформированы и соединение настроено, мы можем отправить запрос и обработать полученный ответ.
// Отправка запроса
Попытка
ОтветHTTP = СоединениеHTTP.Отправить(ЗапросHTTP);
Результат = ОтветHTTP.ПолучитьТелоКакСтроку(); // Или ПолучитьТелоКакДвоичныеДанные() если ожидается бинарный ответ
Если ОтветHTTP.КодСостояния = 200 Тогда
Сообщить("Файл успешно отправлен. Код состояния: " + ОтветHTTP.КодСостояния);
Сообщить("Ответ сервера: " + Результат);
Иначе
Сообщить("Ошибка при отправке файла. Код состояния: " + ОтветHTTP.КодСостояния);
Сообщить("Ответ сервера: " + Результат);
КонецЕсли;
Возврат ОтветHTTP;
Исключение
Сообщить("Ошибка при отправке запроса: " + ОписаниеОшибки());
Возврат Неопределено;
КонецПопытки;
Мы используем блок Попытка...Исключение для корректной обработки возможных ошибок сети или сервера. После отправки запроса мы проверяем КодСостояния ответа. Код 200 обычно означает успех. Тело ответа можно получить с помощью ПолучитьТелоКакСтроку() или ПолучитьТелоКакДвоичныеДанные(), в зависимости от ожидаемого формата.
Объединим все шаги в одну функцию, которая будет отправлять файл.
// Функция для получения сертификата из файла
Функция ПолучитьСертификатИзФайла(ПутьКСертификату, ПарольСертификата = "")
Если Не СтрДлина(ПутьКСертификату) > 0 Тогда
Сообщить("Не указан путь к файлу сертификата.");
Возврат Неопределено;
КонецЕсли;
Попытка
Перем Сертификат;
Если ПарольСертификата = "" Тогда
Сертификат = Новый СертификатOpenSSL(ПутьКСертификату); // Для PEM без пароля или PFX без пароля
Иначе
Сертификат = Новый СертификатOpenSSL(ПутьКСертификату, ПарольСертификата); // Для PFX с паролем
КонецЕсли;
Возврат Сертификат;
Исключение
Сообщить("Ошибка загрузки сертификата: " + ОписаниеОшибки());
Возврат Неопределено;
КонецПопытки;
КонецФункции
// Основная функция для отправки файла
Функция ОтправитьФайлНаСерверССертификатом() Экспорт
// 1. Настройки подключения и файлов
АдресСервераПолный = "https://your.server.com/api/upload"; // !!! Укажите полный URL вашего сервера
ПутьКФайлуДляОтправки = "C:\CHT_00501_UCH_03-00_20250402154720000.zip.pem.pem"; // !!! Укажите полный путь к файлу
ИмяФайлаДляСервера = "CHT_00000_UCH_03-00_20250402154720000.zip.pem.pem"; // Имя файла, под которым он будет принят сервером
ПутьККлиентскомуСертификату = "C:\my_client_cert.pem"; // !!! Укажите путь к вашему клиентскому сертификату
ПарольКлиентскогоСертификата = ""; // Укажите пароль, если он есть, иначе оставьте пустым
// 2. Создание объекта HTTPЗапрос
ЗапросHTTP = Новый HTTPЗапрос(АдресСервераПолный);
ЗапросHTTP.Метод = "POST";
// 3. Установка заголовков аутентификации
ЗапросHTTP.Заголовки.Вставить("X-auth-Subscriber", "501");
ЗапросHTTP.Заголовки.Вставить("X-Auth-Group", "GROUP");
ЗапросHTTP.Заголовки.Вставить("X-Auth-Username", "TestUsr");
ЗапросHTTP.Заголовки.Вставить("X-Auth-Password", "usrte-s@34");
// 4. Формирование тела запроса для multipart/form-data
Граница = "----------WebKitFormBoundary" + Строка(Новый УникальныйИдентификатор);
ЗапросHTTP.Заголовки.Вставить("Content-Type", "multipart/form-data; boundary=" + Граница);
Если Не ФайлСуществует(ПутьКФайлуДляОтправки) Тогда
Сообщить("Файл для отправки не найден по пути: " + ПутьКФайлуДляОтправки);
Возврат Неопределено;
КонецЕсли;
ДанныеФайла = Новый ДвоичныеДанные(ПутьКФайлуДляОтправки);
ТелоЗапроса = "";
ТелоЗапроса = ТелоЗапроса + "--" + Граница + Символы.ПС;
ТелоЗапроса = ТелоЗапроса + "Content-Disposition: form-data; name=""file""; filename=""" + ИмяФайлаДляСервера + """" + Символы.ПС;
ТелоЗапроса = ТелоЗапроса + "Content-Type: application/octet-stream" + Символы.ПС + Символы.ПС;
ТелоЗапроса = ТелоЗапроса + ДанныеФайла.ПолучитьBase64Строку() + Символы.ПС;
ТелоЗапроса = ТелоЗапроса + "--" + Граница + "--" + Символы.ПС;
ЗапросHTTP.УстановитьТелоИзСтроки(ТелоЗапроса, "text/plain; charset=UTF-8");
// 5. Настройка защищенного соединения HTTPS с клиентским сертификатом
КлиентскийСертификат = ПолучитьСертификатИзФайла(ПутьККлиентскомуСертификату, ПарольКлиентскогоСертификата);
Если КлиентскийСертификат = Неопределено Тогда
Возврат Неопределено;
КонецЕсли;
ПараметрыЗащищенногоСоединения = Новый Структура;
ПараметрыЗащищенногоСоединения.Вставить("ПроверятьСервер", Истина);
ПараметрыЗащищенногоСоединения.Вставить("КлиентскийСертификат", КлиентскийСертификат);
ЗащищенноеСоединение = Новый ЗащищенноеСоединениеOpenSSL(ПараметрыЗащищенногоСоединения);
// Извлекаем хост и порт из полного URL для HTTPСоединения
УРИ = Новый УРИ(АдресСервераПолный);
АдресХоста = УРИ.Хост;
ПортХоста = УРИ.Порт;
Если ПортХоста = 0 Тогда // Если порт не указан в URI, используем стандартный для HTTPS
ПортХоста = 443;
КонецЕсли;
СоединениеHTTP = Новый HTTPСоединение(АдресХоста, ПортХоста, , , , , ЗащищенноеСоединение);
// 6. Отправка запроса и обработка ответа
Попытка
ОтветHTTP = СоединениеHTTP.Отправить(ЗапросHTTP);
Результат = ОтветHTTP.ПолучитьТелоКакСтроку();
Если ОтветHTTP.КодСостояния = 200 Тогда
Сообщить("Файл успешно отправлен. Код состояния: " + ОтветHTTP.КодСостояния);
Сообщить("Ответ сервера: " + Результат);
Иначе
Сообщить("Ошибка при отправке файла. Код состояния: " + ОтветHTTP.КодСостояния);
Сообщить("Ответ сервера: " + Результат);
КонецЕсли;
Возврат ОтветHTTP;
Исключение
Сообщить("Ошибка при отправке запроса: " + ОписаниеОшибки());
Возврат Неопределено;
КонецПопытки;
КонецФункции
При работе со сложными HTTP-запросами и интеграциями возникают ошибки. Вот несколько советов по отладке:
Установите программу Fiddler (или аналогичную) на вашей машине. Она позволяет перехватывать и анализировать весь HTTP/HTTPS трафик, проходящий через ваш компьютер. С помощью Fiddler вы можете:
HTTPЗапрос указан корректно, включая протокол (`http://` или `https://`), домен, порт (если он нестандартный) и путь к ресурсу. Ошибки типа "no URL specified" (как в сообщении 3) часто возникают из-за неполного или отсутствующего URL.УстановитьТелоИзСтроки(ТелоЗапроса, "text/plain; charset=UTF-8"), соответствует фактической кодировке вашего тела запроса. Для `multipart/form-data` с Base64 содержимым файла, `UTF-8` обычно подходит.Следуя этим шагам и используя методы отладки, мы сможем успешно реализовать сложный HTTP POST запрос в 1С и наладить интеграцию с внешними системами. Для интерактивной проверки и отладки REST API непосредственно в интерфейсе платформы можно использовать обработку для тестирования HTTP-запросов в 1С.