При интеграции систем на платформе 1С:Предприятие с внешними информационными базами через веб-сервисы разработчики часто сталкиваются с неочевидным поведением платформы. Одной из таких проблем является ошибка вызова конструктора WSОпределения, сопровождающаяся загадочным сообщением: «При создании описания сервиса произошла ошибка. URL сервиса: sfile://?wsdl по причине: Каталог не обнаружен '?wsdl'».
В этой статье мы подробно разберем природу возникновения данной ошибки, проанализируем внутренние механизмы платформы 1С, приводящие к её появлению, и выработаем надежные методы защиты кода, которые навсегда исключат возникновение подобных ситуаций в ваших конфигурациях.
Для того чтобы понять суть проблемы, нам необходимо заглянуть во внутреннюю логику работы платформы 1С при создании описания веб-сервиса. Рассмотрим стандартный конструктор создания веб-сервиса:
Определения = Новый WSОпределения(ПараметрыПодключения.URLReports + "?wsdl", ПараметрыПодключения.Пользователь, ПараметрыПодключения.Пароль);
В штатном режиме переменная ПараметрыПодключения.URLReports содержит валидный URL-адрес публикации веб-сервиса (например, http://server/erp/ws/reports). Платформа конкатенирует его со строкой "?wsdl", получает адрес http://server/erp/ws/reports?wsdl, успешно обращается по протоколу HTTP/HTTPS и загружает схему.
Однако, если переменная URLReports по какой-то причине оказывается пустой строкой, итоговый путь, передаваемый в конструктор, превращается в строку "?wsdl". Проанализируем, как на это реагирует 1С:
http://, https://, ftp://).file:// или в виде относительного пути).sfile:// (Server File)."?wsdl" преобразуется сервером в системный адрес sfile://?wsdl.?wsdl. Поскольку символы вопросов и другие спецсимволы некорректны для имен папок, а сам каталог физически отсутствует, операционная система возвращает ошибку: «Каталог не обнаружен '?wsdl'».Таким образом, появление в ошибке протокола sfile:// — это прямой маркер того, что в платформу был передан пустой или некорректный базовый URL-адрес.
Выяснив, что корень проблемы кроется в пустом адресе, проанализируем типичный код получения параметров подключения, который часто становится источником этой ошибки:
Функция ПолучитьПараметрыПодключенияERP(МакетКомпоновки)
Результат = Новый Структура("Пользователь, Пароль, URLReports", "", "", "");
УстановитьПривилегированныйРежим(Истина);
Попытка
УзелERP = БИТ_СТ_ПовторноеИспользованиеЗначений.ПолучитьЗначениеКонстанты("СТ_НСИ_УзелERP");
Если ЗначениеЗаполнено(УзелERP) Тогда
РеквизитыУзла = ОбщегоНазначения.ЗначенияРеквизитовОбъекта(УзелERP, "URL,Пользователь,Пароль,ИнформационнаяБазаИсточник");
Результат.Пользователь = РеквизитыУзла.Пользователь;
Результат.Пароль = РеквизитыУзла.Пароль;
Результат.URLReports = РеквизитыУзла.URL;
КонецЕсли;
Исключение
// Пустой блок!
КонецПопытки;
УстановитьПривилегированныйРежим(Ложь);
Возврат Результат;
КонецФункции
При детальном рассмотрении этой функции мы можем обнаружить три серьезные архитектурные ловушки, которые гарантируют появление пустых значений параметров.
В строке получения константы используется вызов специализированного модуля: БИТ_СТ_ПовторноеИспользованиеЗначений.ПолучитьЗначениеКонстанты("СТ_НСИ_УзелERP"). Этот общий модуль имеет свойство «Повторное использование возвращаемых значений» (на время вызова или на время сеанса).
Рассмотрим ситуацию по шагам:
СТ_НСИ_УзелERP была пустой (например, база только разворачивается или настройки еще не заданы).ПолучитьЗначениеКонстанты обращается к кэшу и снова мгновенно возвращает старое пустое значение, игнорируя новые данные в базе данных!sfile://?wsdl, вызывая недоумение у администратора, который своими глазами видит заполненные поля в интерфейсе.Решение проблемы кэша: Для применения настроек «на лету» без перезапуска клиентских сеансов необходимо программно сбрасывать кэш повторно используемых модулей. Сделать это можно с помощью встроенного метода платформы:
ОбновитьПовторноИспользуемыеЗначения();
Этот метод очистит кэш текущего сеанса, вынудив платформу при следующем вызове прочитать актуальные настройки непосредственно из базы данных.
Использование пустых блоков Исключение (так называемое «замалчивание» ошибок) — это один из самых опасных антипаттернов в разработке на 1С. В приведенном коде любая критическая ошибка внутри блока Попытка останется незамеченной:
URL, а, например, АдресПодключения или АдресСервера, то метод ОбщегоНазначения.ЗначенияРеквизитовОбъекта вызовет исключение.Из-за пустого блока Исключение программа тихо проглотит эту ошибку. Переменная Результат.URLReports останется пустой строкой, управление передастся дальше, и разработчик получит неинформативное сообщение о каталоге ?wsdl вместо реальной ошибки обращения к метаданным.
Решение проблемы скрытых ошибок: Блок исключения всегда должен фиксировать произошедший сбой в системном Журнале регистрации:
Попытка
// Получение параметров...
Исключение
ЗаписьЖурналаРегистрации("Интеграция.ПодключениеERP",
УровеньЖурналаРегистрации.Ошибка,
,
,
ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
КонецПопытки;
Метод ЗначениеЗаполнено(УзелERP) проверяет лишь то, что в константе выбрана ссылка на объект (например, на конкретный узел плана обмена или элемент справочника). Однако сама по себе заполненная ссылка не гарантирует, что внутри этого объекта заполнены строковые реквизиты адреса и авторизации.
Если узел создан, но поле URL внутри него оставлено пустым, проверка на заполненность узла вернет Истина, а на выходе мы снова получим пустой адрес.
Для того чтобы полностью обезопасить систему от подобных ошибок, спроектируем надежный и понятный алгоритм проверки параметров перед вызовом конструктора WSОпределения. Разберем его реализацию по шагам.
Напишем безопасную функцию получения параметров с обязательным логированием сбоев:
Функция ПолучитьНадежныеПараметрыПодключения() Экспорт
Параметры = Новый Структура;
Параметры.Вставить("Успешно", Ложь);
Параметры.Вставить("Пользователь", "");
Параметры.Вставить("Пароль", "");
Параметры.Вставить("URLReports", "");
Параметры.Вставить("ТекстОшибки", "");
УстановитьПривилегированныйРежим(Истина);
Попытка
УзелERP = БИТ_СТ_ПовторноеИспользованиеЗначений.ПолучитьЗначениеКонстанты("СТ_НСИ_УзелERP");
Если Не ЗначениеЗаполнено(УзелERP) Тогда
Параметры.ТекстОшибки = "В настройках системы не заполнена константа 'СТ_НСИ_УзелERP'!";
Возврат Параметры;
КонецЕсли;
// Читаем реквизиты безопасным методом
РеквизитыУзла = ОбщегоНазначения.ЗначенияРеквизитовОбъекта(УзелERP, "URL, Пользователь, Пароль");
Если ПустаяСтрока(РеквизитыУзла.URL) Тогда
Параметры.ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
"В узле подключения '%1' не заполнен реквизит 'URL'!", СокрЛП(УзелERP));
Возврат Параметры;
КонецЕсли;
Параметры.Пользователь = РеквизитыУзла.Пользователь;
Параметры.Пароль = РеквизитыУзла.Пароль;
Параметры.URLReports = СокрЛП(РеквизитыУзла.URL);
Параметры.Успешно = Истина;
Исключение
Параметры.ТекстОшибки = "Системная ошибка при чтении настроек подключения: " + КраткоеПредставлениеОшибки(ИнформацияОбОшибке());
ЗаписьЖурналаРегистрации("Интеграция.ПараметрыПодключения",
УровеньЖурналаРегистрации.Ошибка,
,,
ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
КонецПопытки;
УстановитьПривилегированныйРежим(Ложь);
Возврат Параметры;
КонецФункции
Теперь реализуем безопасный вызов веб-сервиса с валидацией полученных настроек перед обращением к конструктору:
Процедура ИнициализироватьВебСервис()
ПараметрыПодключения = ПолучитьНадежныеПараметрыПодключения();
Если Не ПараметрыПодключения.Успешно Тогда
// Сообщаем пользователю понятным языком, что именно пошло не так
ВызватьИсключение "Не удалось подключиться к ERP: " + ПараметрыПодключения.ТекстОшибки;
КонецЕсли;
// Формируем результирующий URL
АдресWSDL = ПараметрыПодключения.URLReports;
Если Не СтрЗаканчиваетсяНа(АдресWSDL, "?wsdl") И Не СтрЗаканчиваетсяНа(АдресWSDL, "?WSDL") Тогда
АдресWSDL = АдресWSDL + "?wsdl";
КонецЕсли;
Попытка
// Безопасный вызов конструктора с гарантированно заполненным URL
Определения = Новый WSОпределения(АдресWSDL, ПараметрыПодключения.Пользователь, ПараметрыПодключения.Пароль);
// Дальнейшая работа с веб-сервисом...
Исключение
ВызватьИсключение "Ошибка инициализации WS-соединения: " + КраткоеПредставлениеОшибки(ИнформацияОбОшибке());
КонецПопытки;
КонецПроцедуры
Если вы столкнулись с ошибкой каталога ?wsdl на сервере 1С, выполните следующие действия по шагам:
WSОпределения. Скорее всего, она будет пустой.ОбновитьПовторноИспользуемыеЗначения() или перезапустите сеанс.Попытка...Исключение //КонецПопытки на полноценную запись ошибок в Журнал регистрации, чтобы вовремя локализовать неисправность.