При интеграции 1С:Предприятия со сторонними информационными системами через SOAP-протокол разработчики часто сталкиваются со следующей проблемой: WSDL-описание успешно загружается с внешнего адреса, объект WSПрокси создается без ошибок, но при попытке вызвать любой метод веб-сервиса (например, отправку данных) платформа аварийно завершает работу с ошибкой: «При вызове веб-сервиса произошла ошибка. Неизвестная ошибка. Ошибка работы с Интернет: Не могу установить соединение».
Давайте разберем по шагам причину возникновения этой ситуации и подробно рассмотрим все возможные пути ее решения — от простых программных методов до инфраструктурных настроек.
Секрет кроется в самом устройстве протокола SOAP и структуре файла WSDL. Когда мы инициализируем подключение, платформа 1С скачивает файл XML-описания (WSDL) по внешнему адресу. Однако внутри этого файла, в самом конце, находится секция описания портов и точек подключения <soap:address location="..." />.
Зачастую администраторы стороннего веб-сервиса публикуют его внутри своей корпоративной сети и просто пробрасывают порт наружу. При этом сам веб-сервер «не знает» своего внешнего адреса и при генерации WSDL-файла жестко прописывает туда свой внутренний (локальный) IP-адрес (например, http://192.168.1.50/ws/service или локальное имя сервера в их сети).
В результате происходят следующие процессы:
Прокси.ТочкаПодключения.Местоположение, куда подставляется адрес из скачанного файла (тот самый внутренний IP).Попытка напрямую перезаписать свойство Прокси.ТочкаПодключения.Местоположение = "..." в коде 1С не увенчается успехом, так как это свойство доступно только для чтения. Тем не менее, у нас есть несколько отличных способов обойти это ограничение.
---
Мало кто знает, но стандартный конструктор объекта WSПрокси в платформе 1С:Предприятие поддерживает необязательный 8-й параметр, который позволяет принудительно переопределить адрес точки подключения, полностью проигнорировав некорректный адрес из WSDL-файла.
Полный синтаксис конструктора выглядит так:
Новый WSПрокси(<Определения>, <ПространствоИмен>, <ИмяСервиса>, <ИмяТочкиПодключения>, <ИнтернетПрокси>, <Таймаут>, <ЗащищенноеСоединение>, <Местоположение>, <ИспользоватьАутентификациюОС>)
Проанализируем пример кода, в котором мы явно передаем правильный внешний адрес в качестве восьмого параметра:
// Создаем определения на основе внешнего WSDL-файла
Определения = Новый WSОпределения("http://внешний-хост.ru/service.wsdl");
// Указываем реальный внешний адрес отправки запросов в 8-м параметре
РеальныйАдресСервиса = "http://внешний-хост.ru/service-endpoint";
Прокси = Новый WSПрокси(
Определения,
"http://namespace.uri",
"MyService",
"MyServicePort",
, // Прокси по умолчанию
60, // Таймаут соединения
, // Защищенное соединение по умолчанию
РеальныйАдресСервиса // Наш спасительный параметр!
);
// Теперь вызов метода отработает корректно по внешнему адресу
Результат = Прокси.getGoodsCatalog(СтрокаBase64);
---
Если разработка ведется в типовой конфигурации 1С, созданной на базе БСП, мы можем задействовать готовую функцию-обертку, которая берет на себя всю рутину по созданию и параметризации прокси-объектов. В структуре параметров подключения БСП предусмотрено отдельное свойство для подмены адреса.
Рассмотрим пример реализации через БСП:
ПараметрыПодключения = ОбщегоНазначения.ПараметрыПодключенияWSПрокси();
ПараметрыПодключения.АдресWSDL = "http://внешний-хост.ru/service.wsdl";
ПараметрыПодключения.URIПространстваИмен = "http://namespace.uri";
ПараметрыПодключения.ИмяСервиса = "MyService";
ПараметрыПодключения.ИмяТочкиПодключения = "MyServicePort";
ПараметрыПодключения.Таймаут = 60;
// Явно подменяем точку подключения внешним адресом
ПараметрыПодключения.Местоположение = "http://внешний-хост.ru/service-endpoint";
// Создаем прокси стандартным методом БСП
Прокси = ОбщегоНазначения.СоздатьWSПрокси(ПараметрыПодключения);
Результат = Прокси.getGoodsCatalog(СтрокаBase64);
---
В случаях, когда интеграция построена через дерево метаданных (объект конфигурации WS-ссылка), мы также можем гибко управлять адресом отправки запросов при инициализации соединения. Метод СоздатьWSПрокси() у статической ссылки поддерживает параметр местоположения на 7-й позиции.
Давайте посмотрим на пример программного кода:
// Для статических WS-ссылок параметр местоположения передается 7-м по счету
РеальныйАдресСервиса = "http://внешний-хост.ru/service-endpoint";
Прокси = WSСсылки.ИмяНашейСтатическойСсылки.СоздатьWSПрокси(
"http://namespace.uri",
"MyService",
"MyServicePort",
, // Прокси
60, // Таймаут
, // Защищенное соединение
РеальныйАдресСервиса // Принудительное местоположение
);
Результат = Прокси.getGoodsCatalog(СтрокаBase64);
---
Если по какой-то причине вы работаете на устаревшей версии платформы 1С, где вышеописанные параметры не отрабатывают должным образом, можно применить надежный и гибкий обходной маневр: программно скачать файл WSDL, заменить некорректные адреса в XML-коде на лету и загрузить измененный файл из локального кэша.
Давайте пошагово реализуем этот алгоритм в коде:
// Шаг 1. Настраиваем подключение для скачивания исходного файла
НТТРСоединение = Новый HTTPСоединение("внешний-хост.ru", , , , , 30);
Запрос = Новый HTTPЗапрос("/service.wsdl");
Ответ = НТТРСоединение.Получить(Запрос);
Если Ответ.КодСостояния = 200 Тогда
// Получаем текст WSDL
ТекстWSDL = Ответ.ПолучитьТелоКакСтроку();
// Шаг 2. Заменяем внутренний IP стороннего сервера на их же внешний домен
НеправильныйАдрес = "192.168.1.50:8080/ws_path";
ПравильныйАдрес = "внешний-хост.ru/ws_path";
ТекстWSDL = СтрЗаменить(ТекстWSDL, НеправильныйАдрес, ПравильныйАдрес);
// Шаг 3. Записываем модифицированный XML во временный файл на диске сервера
ИмяВременногоФайла = ПолучитьИмяВременногоФайла("wsdl");
ЗаписьТекста = Новый ЗаписьТекста(ИмяВременногоФайла, КодировкаТекста.UTF8);
ЗаписьТекста.Записать(ТекстWSDL);
ЗаписьТекста.Закрыть();
// Шаг 4. Инициализируем соединение 1С, используя схему "file://"
ПутьКФайлуСхемы = "file://" + СтрЗаменить(ИмяВременногоФайла, "\", "/");
Определения = Новый WSОпределения(ПутьКФайлуСхемы);
// Шаг 5. Создаем прокси. Теперь внутри схемы прописаны корректные адреса отправки!
Прокси = Новый WSПрокси(Определения, "http://namespace.uri", "MyService", "MyServicePort");
Результат = Прокси.getGoodsCatalog(СтрокаBase64);
// Удаляем временный файл за собой
Попытка
УдалитьФайлы(ИмяВременногоФайла);
Исключение
// Игнорируем ошибку удаления временного файла
КонецПопытки;
КонецЕсли;
---
Если у вас есть контакт со службой системного администрирования со стороны владельцев веб-сервиса, наиболее правильным архитектурным решением будет корректная настройка их веб-сервера (например, Nginx).
Для того чтобы веб-сервис генерировал WSDL с правильными внешними путями автоматически, администраторам сторонней системы необходимо выполнить следующие действия:
X-Forwarded-Host, X-Forwarded-Proto и X-Forwarded-Port на реверс-прокси сервере.ngx_http_sub_module в Nginx), чтобы заменять внутренний IP-адрес на внешнее доменное имя в WSDL-ответах «на лету» перед отдачей клиенту.Применение любого из предложенных программных решений в коде 1С гарантирует стабильную интеграцию с веб-сервисом и избавит вас от необходимости разворачивать сложные VPN-туннели ради обхода некорректно сформированных WSDL-схем.