При интеграции 1С с внешними веб-сервисами и сайтами разработчики нередко сталкиваются с ситуацией, когда отправка файла через HTTP-запрос намертво подвешивает клиентский сеанс. Ситуация усугубляется в файловых информационных базах: зависание одного сеанса приводит к полной недоступности базы для всех остальных пользователей. При этом блок Попытка ... Исключение не срабатывает, ошибки не фиксируются в Журнале регистрации, а под одними учетными записями обмен проходит успешно, а под другими вызывает зависание. Разберем по шагам, почему возникает эта проблема и как правильно настроить сетевое взаимодействие в платформе 1С:Предприятие.
Проанализируем механику возникновения ошибки. Когда программа выполняет сетевой вызов (например, через метод ОтправитьДляОбработки или ВызватьHTTPМетод), платформа открывает сетевой сокет и ожидает ответа от удаленного сервера.
Выделим ключевые факторы, приводящие к зависанию:
HTTPСоединение не задан параметр таймаута, платформа ожидает ответ бесконечно. Поскольку сетевой разрыв не происходит, код не переходит в ветку Исключение.НачатьТранзакцию() или неявной в обработчиках событий объекта ПередЗаписью, ПриЗаписи), в файловом режиме блокируется файл *.1CD. Пока первый пользователь ожидает завершения запроса, работа остальных сеансов полностью парализуется.Выясним причину зависания на уровне операционной системы. Если проблема связана с работой защитного ПО, необходимо настроить правила фильтрации трафика:
1cv8.exe и 1cv8c.exe в список доверенных программ антивируса и брандмауэра.Посмотрим на частую ошибку: свойство Таймаут у объекта HTTPСоединение доступно только для чтения, поэтому попытка установить его напрямую (например, Соединение.Таймаут = 10;) вызовет исключение «Поле объекта недоступно для записи».
Таймаут задается строго через конструктор объекта. Разберем синтаксис конструктора платформы:
Новый HTTPСоединение(<Сервер>, <Порт>, <Пользователь>, <Пароль>, <Прокси>, <Таймаут>, <ЗащищенноеСоединение>, <ИспользоватьАутентификациюОС>)
Обратите внимание на строгую позиционность параметров. Параметр Таймаут идет шестым по счету. Если прокси-сервер не используется, на его место обязательно нужно передать Неопределено, иначе числовое значение таймаута попадет в параметр прокси и соединение завершится с ошибкой.
Рассмотрим правильный пример инициализации защищенного соединения с явным ограничением времени ожидания в 30 секунд:
Сервер = "api.mysite.ru";
Порт = 443;
Таймаут = 30; // Время ожидания в секундах
// Инициализируем защищенное SSL-соединение
Защита = Новый ЗащищенноеСоединениеOpenSSL();
Попытка
Соединение = Новый HTTPСоединение(
Сервер,
Порт,
, // Пользователь веб-сервиса (если не требуется — пропускаем)
, // Пароль веб-сервиса
Неопределено, // Прокси (обязательно Неопределено, чтобы не сбить позицию таймаута)
Таймаут,
Защита
);
Исключение
ЗаписьЖурналаРегистрации("HTTPОбмен", УровеньЖурналаРегистрации.Ошибка,,,
"Не удалось создать HTTPСоединение: " + ОписаниеОшибки());
ВызватьИсключение;
КонецПопытки;
Рассмотрим современный способ отправки файлов на сервер. Устаревший метод ОтправитьДляОбработки с передачей путей файлов напрямую имеет ряд ограничений по управлению заголовками. Рекомендуется использовать связку объектов HTTPЗапрос и HTTPОтвет.
Посмотрим на пример отправки файла методом POST с контролем результата:
АдресРесурса = "/api/v1/upload";
ПутьКФайлу = "C:\temp\upload_data.zip";
// Формируем HTTP-запрос
Запрос = Новый HTTPЗапрос(АдресРесурса);
Запрос.Заголовки.Вставить("Content-Type", "application/octet-stream");
Запрос.Заголовки.Вставить("Accept", "application/json");
// Устанавливаем файл в тело запроса
Запрос.УстановитьИмяФайлаТела(ПутьКФайлу);
Попытка
// Отправляем запрос и получаем объект HTTPОтвет
Ответ = Соединение.ОтправитьДляОбработки(Запрос);
Если Ответ.КодСостояния = 200 ИЛИ Ответ.КодСостояния = 201 Тогда
ТекстОтвета = Ответ.ПолучитьТелоКакСтроку();
// Обработка успешного ответа сервера
Иначе
ЗаписьЖурналаРегистрации("HTTPОбмен", УровеньЖурналаРегистрации.Предупреждение,,,
СтрШаблон("Сервер вернул код ошибки: %1. Ответ: %2", Ответ.КодСостояния, Ответ.ПолучитьТелоКакСтроку()));
КонецЕсли;
Исключение
// Сюда программа попадет, если сработает таймаут или оборвется сеть
ЗаписьЖурналаРегистрации("HTTPОбмен", УровеньЖурналаРегистрации.Ошибка,,,
"Ошибка при отправке данных: " + ОписаниеОшибки());
ВызватьИсключение;
КонецПопытки;
Чтобы отправка файлов не мешала работе других пользователей в файловой базе, придерживаемся следующих правил:
ФоновыеЗадания.Выполнить(). Это сохранит отзывчивость пользовательского интерфейса даже при длительной сетевой операции.