Интеграция систем с помощью SOAP веб-сервисов в 1С — это мощный инструмент, однако у начинающих разработчиков этот процесс часто вызывает трудности. Ошибки авторизации, падение платформы с кодами ответов 400 или 500, а также зависание интерфейса — типичные спутники первых попыток работы с WSПрокси. В этой статье мы подробно разберем, как правильно организовать подключение к веб-сервису, обработать возможные исключения и проверить доступность сервиса без риска заморозить сеанс пользователя.
Проанализируем основные проблемы, с которыми сталкиваются разработчики при написании кода подключения к веб-сервисам:
WSОпределения часто возникают из-за того, что путь к WSDL-описанию сформирован некорректно. Например, если адрес источника или имя веб-сервиса получены с ошибкой (или вовсе вернули пустую строку), платформа пытается обратиться к несуществующему адресу вроде ?wsdl.CalcLengthString, часто используемый в учебных статьях, не является универсальным методом для проверки связи. WSПрокси динамически создает методы на основе WSDL-схемы вашего веб-сервиса. Если в вашем сервисе нет функции CalcLengthString, вы получите ошибку "Значение не является значением объектного типа". Вызывать нужно только те методы, которые реально опубликованы в вашем веб-сервисе (например, ПолучитьОтчет).WSОпределения, так и в свойства созданного WSПрокси.WSОпределения делает синхронный сетевой запрос для скачивания WSDL-схемы. Если удаленный сервер "лежит" или долго отвечает, ваша программа зависнет на стандартный таймаут операционной системы (который может достигать нескольких минут). В цикле опроса нескольких баз это абсолютно недопустимо.Выясним причину ошибки HTTP 400 из примера автора. Попытка получить адрес источника следующим образом:
АдресИсточника = Справочники.Константы.АдресИсточника.Описание; // Ошибка!
В платформе 1С не существует коллекции Справочники.Константы. Если вам нужно получить значение стандартной константы 1С, используйте менеджер констант. Рассмотрим правильный синтаксис:
АдресИсточника = Константы.АдресИсточника.Получить();
Если же в вашей конфигурации действительно создан справочник с именем "Константы" (что является крайне нерекомендуемым стилем разработки), то обращаться напрямую к менеджеру справочника для получения реквизита элемента нельзя. Сначала нужно найти конкретную ссылку (например, предопределенный элемент или через поиск), и только потом брать его реквизит.
Для того чтобы детально понимать, на каком этапе происходит сбой (скачивание WSDL или авторизация на веб-сервисе), мы должны разделить создание определений и создание прокси, а также корректно обработать ошибки. Посмотрим на пример отказоустойчивой функции:
&НаСервере
Функция ПолучитьПроксиВебСервиса(АдресИсточника, URIИмен, ИмяВебСервиса, Логин, Пароль, Таймаут = 5) Экспорт
// Формируем корректный URL для получения WSDL-описания
АдресWSDL = СокрЛП(АдресИсточника);
Если Не Прав(АдресWSDL, 1) = "/" Тогда
АдресWSDL = АдресWSDL + "/";
КонецЕсли;
АдресWSDL = АдресWSDL + ИмяВебСервиса + "?wsdl";
ИмяТочкиПодключения = ИмяВебСервиса + "Soap";
Попытка
// Создаем WSОпределения с явным указанием таймаута (доступно в 1С 8.3)
Определения = Новый WSОпределения(АдресWSDL, Логин, Пароль, , Таймаут);
Исключение
ЗаписьЖурналаРегистрации("Интеграция.ВебСервисы",
УровеньЖурналаРегистрации.Ошибка, , ,
"Ошибка создания WSОпределений для " + АдресWSDL + Символы.ПС + ОписаниеОшибки());
Возврат Неопределено;
КонецПопытки;
Попытка
// Создаем прокси на основе полученных определений
Прокси = Новый WSProxy(Определения, URIИмен, ИмяВебСервиса, ИмяТочкиПодключения);
Прокси.Пользователь = Логин;
Прокси.Пароль = Пароль;
Возврат Прокси;
Исключение
ЗаписьЖурналаРегистрации("Интеграция.ВебСервисы",
УровеньЖурналаРегистрации.Ошибка, , ,
"Ошибка создания WSПрокси для " + ИмяВебСервиса + Символы.ПС + ОписаниеОшибки());
Возврат Неопределено;
КонецПопытки;
КонецФункции
Важный совет: Не создавайте тяжелый объект WSОпределения в цикле для проверки "жив ли сервис". Гораздо быстрее и правильнее отправить легкий пинг через HTTP-соединение с минимальным таймаутом. Если веб-сервер отвечает кодом 200 (OK) или даже 401 (Unauthorized — значит, сервер жив, просто нужна авторизация), только тогда мы тратим ресурсы на создание SOAP-соединения.
Рассмотрим, как реализовать такую проверку:
&НаСервере
Функция СерверДоступен(АдресИсточника, ИмяВебСервиса, Таймаут = 3)
// Парсим адрес источника, чтобы выделить хост и путь
// Для простоты примера предполагаем, что адрес начинается с http:// или https://
Протокол = "";
АдресОчищенный = АдресИсточника;
Если Найти(АдресИсточника, "https://") > 0 Тогда
Протокол = "https://";
АдресОчищенный = Сред(АдресИсточника, 9);
ИначеЕсли Найти(АдресИсточника, "http://") > 0 Тогда
Протокол = "http://";
АдресОчищенный = Сред(АдресИсточника, 8);
КонецЕсли;
// Разделяем хост и путь публикации
ПозицияСлэша = Найти(АдресОчищенный, "/");
Если ПозицияСлэша = 0 Тогда
Хост = АдресОчищенный;
Путь = "/" + ИмяВебСервиса + "?wsdl";
Иначе
Хост = Лев(АдресОчищенный, ПозицияСлэша - 1);
Путь = Сред(АдресОчищенный, ПозицияСлэша) + "/" + ИмяВебСервиса + "?wsdl";
КонецЕсли;
// Настраиваем защищенное соединение, если используется https
ЗащищенноеСоединение = Неопределено;
Если Протокол = "https://" Тогда
ЗащищенноеСоединение = Новый ЗащищенноеСоединениеOpenSSL();
КонецЕсли;
Попытка
HTTPСоединение = Новый HTTPСоединение(Хост, , , , , Таймаут, ЗащищенноеСоединение);
HTTPЗапрос = Новый HTTPЗапрос(Путь);
HTTPОтвет = HTTPСоединение.Получить(HTTPЗапрос);
// Коды 200 (ОК) или 401 (Требуется авторизация) свидетельствуют о том, что веб-сервер работает
Возврат (HTTPОтвет.КодСостояния = 200 Или HTTPОтвет.КодСостояния = 401);
Исключение
Возврат Ложь;
КонецПопытки;
КонецФункции
Теперь объединим все части решения в один рабочий алгоритм. Мы будем обходить строки нашей таблицы значений (ТЗ), проверять доступность физического сервера быстрым запросом, и в случае успеха подключаться по SOAP и вызывать реальный метод веб-сервиса (например, простейший метод проверки авторизации или пинга, созданный в вашей базе-приемнике).
&НаСервере
Процедура ПроверитьДоступностьВебСервисовНаСервере()
// Получаем базовый адрес из констант (правильный метод!)
АдресСервиса = Константы.АдресИсточника.Получить();
URIИмен = "http://www.mycompany.ru/integration"; // Укажите ваш URI пространства имен
Логин = "Администратор";
Пароль = "ВашПароль";
Для Каждого Стр Из Объект.ТЗВебСервисов Цикл
ИмяВебСервиса = Стр.ВебСервис; // Берем имя сервиса из строки ТЗ
// Этап 1: Быстрый пинг веб-сервера
Если Не СерверДоступен(АдресСервиса, ИмяВебСервиса, 3) Тогда
Стр.Доступность = Ложь;
Продолжить;
КонецЕсли;
// Этап 2: Попытка создания SOAP-соединения и вызова тестового метода
Прокси = ПолучитьПроксиВебСервиса(АдресСервиса, URIИмен, ИмяВебСервиса, Логин, Пароль, 5);
Если Прокси = Неопределено Тогда
Стр.Доступность = Ложь;
Продолжить;
КонецЕсли;
Попытка
// ВНИМАНИЕ: Вызываем РЕАЛЬНЫЙ метод вашего веб-сервиса, а не шаблонный "CalcLengthString"
// Рекомендуется сделать легковесную функцию Ping() в самом веб-сервисе базы-приемника
РезультатВызова = Прокси.Ping();
Если РезультатВызова = Истина Тогда
Стр.Доступность = Истина;
Иначе
Стр.Доступность = Ложь;
КонецЕсли;
Исключение
// Метод Ping не реализован или произошла внутренняя ошибка выполнения кода в базе-приемнике
ЗаписьЖурналаРегистрации("Интеграция.ВебСервисы",
УровеньЖурналаРегистрации.Предупреждение, , ,
"Не удалось выполнить метод Ping для сервиса " + ИмяВебСервиса + Символы.ПС + ОписаниеОшибки());
Стр.Доступность = Ложь;
КонецПопытки;
КонецЦикла;
КонецПроцедуры
Для стабильной работы интеграций через веб-сервисы всегда придерживайтесь следующих правил:
WSОпределения и HTTPСоединение, чтобы избежать зависания сеансов пользователей при падении серверов.CalcLengthString), создавайте свои простые служебные функции проверки связи на стороне веб-сервиса приемника.