При разработке интеграционных решений между 1С:Предприятием и сторонними информационными системами (например, внешними CRM, ERP или веб-сервисами) разработчики регулярно сталкиваются с проблемой: при попытке импортировать файл описания веб-службы (WSDL) платформа 1С возвращает ошибку «Неверный формат». При этом разработчики стороннего ресурса уверяют, что их сервис полностью исправен, отлично открывается в браузере и без проблем импортируется в такие инструменты тестирования, как SoapUI.
В этой статье мы подробно разберем, почему возникает подобный конфликт, как доказать невалидность схемы разработчикам стороннего сервиса и какие обходные пути может использовать 1С-программист, чтобы успешно завершить интеграцию, даже если внешняя сторона отказывается вносить изменения в свои файлы конфигурации.
Для начала проанализируем ситуацию и поймем, почему возникает такое разногласие между системами. Инструменты вроде SoapUI, а также веб-библиотеки на языках PHP (SoapClient), Python (Zeep) или Java (JAX-WS) изначально проектировались с расчетом на максимальную «всеядность» и отказоустойчивость. Обнаружив неточности или мелкие несоответствия стандартам в WSDL-файле, они пытаются самостоятельно «достроить» недостающую логику, игнорируя предупреждения.
Платформа 1С:Предприятие работает принципиально иначе. Встроенный в нее парсер XML/XSD базируется на строгом соблюдении стандартов консорциума W3C и спецификации WS-I Basic Profile 1.1. Если схема WSDL содержит хотя бы минимальное отклонение от этих стандартов, 1С не будет пытаться угадать логику разработчиков стороннего сервиса, а мгновенно прервет операцию и выбросит исключение.
Рассмотрим наиболее типичные технические ошибки в сторонних WSDL-файлах, на которых «спотыкается» парсер 1С:
rpc/encoded. Однако современные стандарты WS-I требуют обязательного применения стиля document/literal. Платформа 1С крайне плохо работает с кодированными типами данных.Array из пространства имен http://schemas.xmlsoap.org/soap/encoding/. С точки зрения строгой схемы XSD, стандартного типа Array не существует. Корректный способ описания списков и массивов для 1С — это использование стандартной последовательности xsd:sequence с указанием атрибута maxOccurs="unbounded".<wsp:Policy>). Парсер 1С не умеет обрабатывать эти конструкции «из коробки» и аварийно завершает работу.xsd:import, но в атрибуте schemaLocation указан невалидный или недоступный относительный URL-адрес, 1С не сможет загрузить типы данных. Также критичны пустые или дефолтные пространства имен (вроде tempuri.org), если они настроены некорректно.Если разработчики сторонней CRM настаивают на валидности своего сервиса, ссылаясь на его успешную работу в SoapUI, мы можем использовать их же инструмент для демонстрации ошибок. Дело в том, что в SoapUI встроен профессиональный инструмент проверки соответствия веб-сервисов международным стандартам. Выполним проверку по шагам:
Полученный отчет содержит ссылки на конкретные строки спецификации W3C. Предоставим этот документ коллегам со стороны CRM — против анализа их собственного инструмента тестирования у них не останется аргументов.
Если процесс согласования изменений затягивается, а интеграцию нужно запускать прямо сейчас, применим самый популярный среди 1С-разработчиков обходной путь — локальную коррекцию схемы.
Разберем этот алгоритм по шагам:
service.wsdl.xsd:import, скачаем их аналогичным образом и сохраним в ту же папку.service.wsdl в текстовом или XML-редакторе (например, Notepad++ или XMLSpy) и вручную исправим проблемные места:
<wsp:Policy>...</wsp:Policy> и их упоминания.soapenc:Array на стандартные последовательности XML.xsd:import пропишем локальные пути к скачанным файлам схем вместо внешних интернет-ссылок.service.wsdl. Платформа успешно импортирует корректную схему и создаст все необходимые типы данных 1С.Посмотрим на пример реализации данного подхода в коде 1С:
// Определим пути к локальному файлу описания и параметры подключения
ПутьКЛокальномуWSDL = "C:\WSDL\fixed_service.wsdl";
АдресБоевогоСервиса = "https://crm.external-system.ru/api/soap/v1";
ИмяТочкиПодключения = "CrmSoapEndpoint";
ИмяСервиса = "CrmService";
ПространствоИмен = "http://crm.external-system.ru/soap/";
Попытка
// Инициализируем определения из локально сохраненного и исправленного файла
Определения = Новый WSОпределения(ПутьКЛокальномуWSDL);
// Создаем прокси, переопределяя реальный адрес вызова
Прокси = Новый WSПрокси(Определения, ПространствоИмен, ИмяСервиса, ИмяТочкиПодключения);
Прокси.АдресВызова = АдресБоевогоСервиса;
// Теперь мы можем вызывать любые методы веб-сервиса
ПараметрЗапроса = Прокси.ФабрикаXDTO.Создать(Прокси.ФабрикаXDTO.Пакеты.Получить(ПространствоИмен).Получить("ClientRequest"));
ПараметрЗапроса.ClientId = "100234";
Результат = Прокси.GetClientData(ПараметрЗапроса);
ПоказатьПредупреждение(, "Данные успешно получены для клиента: " + Результат.ClientName);
Исключение
ЗаписатьЛогОшибок(ОписаниеОшибки());
ВызватьИсключение "Не удалось выполнить интеграционный запрос к CRM. Обратитесь к администратору.";
КонецПопытки;
Рассмотрим ситуацию, когда WSDL-файл сторонней системы настолько запутать или поврежден, что его ручная правка занимает непозволительно много времени, а структура типов постоянно меняется. В этом случае лучшим и наиболее отказоустойчивым решением будет полный отказ от встроенных объектов 1С WSСсылки и WSПрокси.
Мы можем взаимодействовать с SOAP-сервисом напрямую, отправляя обычные текстовые POST-запросы с XML-конвертом (Envelope) внутри. Это избавит платформу 1С от необходимости парсить WSDL-схему перед каждым вызовом.
Разберем этот процесс по шагам:
ЗаписьXML.HTTPСоединение.text/xml; charset=utf-8 и, при необходимости, заголовок SOAPAction (его точное значение также можно скопировать из свойств запроса в SoapUI).ЧтениеXML или ПостроительDOM.Проанализируем пример кода, реализующий прямую отправку SOAP-запроса через HTTP-протокол:
// Сформируем тело SOAP-конверта с подстановкой параметров
ИдентификаторКлиента = "100234";
ТелоSOAP =
"<soapenv:Envelope xmlns:soapenv=""http://schemas.xmlsoap.org/soap/envelope/"" xmlns:crm=""http://crm.external-system.ru/soap/"">
| <soapenv:Header/>
| <soapenv:Body>
| <crm:GetClientData>
| <crm:ClientId>" + ИдентификаторКлиента + "</crm:ClientId>
| </crm:GetClientData>
| </soapenv:Body>
|</soapenv:Envelope>";
// Настроим параметры HTTP-соединения с поддержкой шифрования SSL
СерверCRM = "crm.external-system.ru";
ПортCRM = 443;
SSLСоединение = Новый ЗащищенноеСоединениеOpenSSL();
HTTPСоединение = Новый HTTPСоединение(СерверCRM, ПортCRM, , , , 30, SSLСоединение);
// Создаем HTTP-запрос к точке обслуживания API
HTTPЗапрос = Новый HTTPЗапрос("/api/soap/v1");
HTTPЗапрос.Заголовки.Вставить("Content-Type", "text/xml; charset=utf-8");
HTTPЗапрос.Заголовки.Вставить("SOAPAction", "http://crm.external-system.ru/soap/GetClientData");
HTTPЗапрос.УстановитьТелоИзСтроки(ТелоSOAP, КодировкаТекста.UTF8, ИспользованиеByteOrderMark.НеИспользовать);
Попытка
// Отправляем POST-запрос на сервер CRM
HTTPОтвет = HTTPСоединение.ОтправитьДляОбъекта(HTTPЗапрос);
Если HTTPОтвет.КодСостояния = 200 Тогда
// Читаем полученный XML-ответ
ТекстОтвета = HTTPОтвет.ПолучитьТелоКакСтроку(КодировкаТекста.UTF8);
Чтение = Новый ЧтениеXML;
Чтение.УстановитьСтроку(ТекстОтвета);
// Разбираем структуру XML для поиска нужного узла с данными
Пока Чтение.Прочитать() Цикл
Если Чтение.ТипУзла = ТипУзлаXML.НачалоЭлемента И Чтение.Имя = "ClientName" Тогда
Чтение.Прочитать(); // Переходим к значению внутри тега
ИмяКлиента = Чтение.Значение;
Прервать;
КонецЕсли;
КонецЦикла;
ПоказатьПредупреждение(, "Прямой HTTP-вызов выполнен успешно! Клиент: " + ИмяКлиента);
Иначе
ВызватьИсключение "Ошибка сервера CRM. Код ответа: " + HTTPОтвет.КодСостояния;
КонецЕсли;
Исключение
ЗаписатьЛогОшибок(ОписаниеОшибки());
ВызватьИсключение "Ошибка передачи данных по HTTP-каналу.";
КонецПопытки;
Этот способ является наиболее универсальным и надежным («неубиваемым»). Даже если разработчики сторонней CRM кардинально изменят структуру своего WSDL или добавят туда несовместимые с 1С библиотеки шифрования, наш HTTP-запрос продолжит корректно отправлять структурированные XML-пакеты и принимать ответы, гарантируя бесперебойную работу интеграции.