Как исправить ошибки проверки данных XDTO и настроить стабильное подключение к внешним SOAP веб-сервисам в 1С

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При интеграции систем 1С:Предприятие с внешними веб-сервисами разработчики регулярно сталкиваются с трудностями. Проблемы могут возникать как на этапе импорта WSDL-описания в конфигуратор, так и во время непосредственного выполнения SOAP-запросов из кода. Разберем детально две наиболее распространенные и сложные ситуации: ошибку компиляции регулярного выражения XDTO (xmlFAParseRegExp: extra characters) и ошибки сетевого соединения/авторизации (на примере интеграции с API «Броневик»).

---

Часть 1. Ошибка проверки данных XDTO: Несоответствие фасету Pattern

Рассмотрим ситуацию, когда при импорте WSDL-ссылки в дерево конфигурации или при создании WS-прокси платформа 1С выдает ошибку вида:

«Ошибка проверки данных XDTO: Значение не соответствует простому типу... Несоответствие фасету Pattern... failed to compile: xmlFAParseRegExp: extra characters».

Выясним причину этой ошибки. Платформа 1С для разбора XML-схем, импорта WSDL и валидации типов данных XDTO использует стандартную системную библиотеку libxml2. Согласно спецификации W3C XML Schema, символ косой черты (/) не является управляющим спецсимволом в регулярных выражениях. Следовательно, его экранирование обратным слэшем (\/) считается синтаксической ошибкой.

Регулярное выражение из нашего примера (0?[1-9]|1[0-2])\/[0-9]{2} предназначено для проверки формата даты «месяц/двузначный год» (например, 05/24). Сторонние инструменты тестирования API (такие как SOAP UI, Postman) или веб-браузеры используют более либеральные движки регулярных выражений (например, JavaScript-совместимые или PCRE), которые молча игнорируют избыточное экранирование обычных символов. Парсер 1С строго следует стандарту XML Schema: встречая символ \ перед /, он пытается распознать его как управляющую последовательность и, не найдя совпадений, аварийно завершает компиляцию схемы.

Для решения этой проблемы мы можем использовать три основных подхода.

Способ 1. Локальная корректировка схемы WSDL/XSD

Если поставщик услуг не может оперативно исправить ошибку на своей стороне, мы можем скорректировать схему самостоятельно:

  1. Откроем ссылку WSDL в браузере и сохраним её содержимое как локальный файл с расширением .wsdl на диск.
  2. Если в WSDL есть импортируемые внешние схемы (теги <xsd:import>), скачаем их файлы XML/XSD аналогичным образом.
  3. Откроем скачанный файл в любом текстовом редакторе (например, Notepad++, VS Code).
  4. Найдем ошибочный паттерн регулярного выражения и заменим экранированную последовательность \/ на обычный символ /. В нашем случае выражение примет вид: (0?[1-9]|1[0-2])/[0-9]{2}.
  5. Сохраним изменения. При добавлении WS-ссылки в конфигуратор укажем путь к нашему локальному файлу вместо URL-адреса.

Способ 2. Динамическое исправление схемы в коде 1С («на лету»)

Если нам необходимо создавать WS-прокси динамически в коде без добавления статической WS-ссылки в конфигуратор, мы можем автоматизировать процесс скачивания и очистки WSDL-файла перед инициализацией прокси. Посмотрим на пример реализации такого механизма:


// Функция получает валидный временный файл WSDL с исправленными регулярными выражениями
Функция ПолучитьИсправленныйФайлWSDL(ИсходныйURL) Экспорт
    
    // Выполняем загрузку исходного WSDL через HTTP-соединение
    ИмяФайлаWSDL = ПолучитьИмяВременногоФайла("wsdl");
    
    // Разбиваем URL на хост и путь для HTTPСоединения
    СтруктураURL = URLРазбрать(ИсходныйURL); 
    
    // Настраиваем защищенное соединение, если это HTTPS
    БезопасноеСоединение = Неопределено;
    Если СтруктураURL.Схема = "https" Тогда
        БезопасноеСоединение = Новый ЗащищенноеСоединениеOpenSSL();
    КонецЕсли;
    
    Соединение = Новый HTTPСоединение(СтруктураURL.Хост, СтруктураURL.Порт, , , , 30, БезопасноеСоединение);
    Запрос = Новый HTTPЗапрос(СтруктураURL.ПутьНаСервере);
    
    Попытка
        РезультатЗапроса = Соединение.Получить(Запрос, ИмяФайлаWSDL);
        Если РезультатЗапроса.КодСостояния <> 200 Тогда
            ВызватьИсключение "Ошибка загрузки WSDL. Код состояния: " + РезультатЗапроса.КодСостояния;
        КонецЕсли;
    Исключение
        ЗаписьЖурналаРегистрации("Интеграция.WSDL", УровеньЖурналаРегистрации.Ошибка, , , ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
        ВызватьИсключение "Не удалось загрузить WSDL-описание сервиса.";
    КонецПопытки;
    
    // Считываем текст файла для корректировки регулярных выражений
    ТекстовыйДокумент = Новый ТекстовыйДокумент;
    ТекстовыйДокумент.Прочитать(ИмяФайлаWSDL, КодировкаТекста.UTF8);
    ТекстWSDL = ТекстовыйДокумент.ПолучитьТекст();
    
    // Исправляем ошибочный экранированный слэш в регулярных выражениях
    ТекстWSDL = СтрЗаменить(ТекстWSDL, "\/", "/");
    
    // Записываем исправленный текст обратно во временный файл
    ТекстовыйДокумент.УстановитьТекст(ТекстWSDL);
    ТекстовыйДокумент.Записать(ИмяФайлаWSDL, КодировкаТекста.UTF8);
    
    Возврат ИмяФайлаWSDL;
    
КонецФункции

После получения пути к исправленному временному файлу мы можем инициализировать WSПрокси, передав локальный путь в качестве параметра источника WSDL.

---

Часть 2. Ошибки подключения и авторизации при вызове веб-сервисов

Перейдем ко второй проблеме, описанной в сообщениях форума. Разработчик пытается подключиться к API «Броневик» (поставщик бронирования отелей) и сталкивается с ошибками: либо «Неверные данные авторизации» при вызове метода, либо «Не могу установить соединение» при попытках изменения протокола.

Проанализируем по шагам причины этого поведения и способы их устранения.

1. Ошибки SSL-сертификатов при обращении к хосту по IP-адресу

В коде автора используется обращение напрямую по IP-адресу: https://195.201.135.150/v2.1.0/api.php?wsdl. При этом в параметрах создания WSПрокси передается объект Новый ЗащищенноеСоединениеOpenSSL.

В чем здесь кроется ошибка? Любой коммерческий SSL-сертификат выдается на доменное имя (например, *.bronevik.com), а не на голый IP-адрес. Когда платформа 1С инициирует защищенное TLS/SSL соединение по IP-адресу, она выполняет валидацию хоста (Host Name Verification). Обнаружив несоответствие между запрашиваемым IP-адресом и именами хостов в предоставленном сервером сертификате, платформа разрывает соединение из соображений безопасности. SOAP UI может игнорировать такие несоответствия, если в его настройках отключена проверка SSL-сертификатов (опция «SSL Hostname Verification» отключена), но 1С делает эту проверку строго.

Решение: Всегда используйте официальное доменное имя сервиса вместо IP-адреса. Замените URL-адреса подключения на доменные имена, предоставленные технической поддержкой интегратора (например, https://api.bronevik.com/v2.1.0/api.php?wsdl).

2. Почему SOAP UI успешно выполняет запрос, а 1С выдает «Не могу установить соединение»?

Это классическая проблема разницы контекстов выполнения кода. Разберем ключевые аспекты:

Чтобы сервер 1С смог успешно достучаться до внешнего ресурса, системному администратору необходимо:

  1. Проверить настройки прокси-сервера для сервера 1С. При необходимости прописать их в конфигурационном файле inetcfg.xml платформы 1С или использовать программную установку параметров прокси в коде через УстановитьНастройкиПроксиСервера().
  2. В правилах брандмауэра (файрвола) на сервере приложений 1С разрешить исходящий трафик для процесса rphost.exe на порты 80 (HTTP) и 443 (HTTPS).

3. Проблема «Неверные данные авторизации» из-за специфики сериализации XDTO

Даже если сетевое соединение установлено и параметры авторизации (логин, пароль) переданы верно, SOAP-сервер получателя может отклонить запрос с ошибкой авторизации. Это происходит из-за различий в формировании структуры XML-пакета XML-сериализатором платформы 1С и стандартом, который ожидает принимающий сервер (часто построенный на базе PHP или Java).

При использовании автоматического механизма WSПрокси платформа 1С может опускать пространства имен (namespaces) для вложенных тегов структуры параметров или, наоборот, добавлять пустые префиксы (например, xmlns=""). В результате принимающая сторона не может сопоставить пришедшие XML-элементы со своими классами данных и «не видит» переданные логин и пароль внутри структуры Credentials.

Что делать в этом случае? Самый надежный, гибкий и гарантирующий 100% успех метод — это отказ от использования высокоуровневого объекта WSПрокси в пользу низкоуровневой работы с сервисом через HTTPСоединение.

---

Часть 3. Универсальное решение: Ручное формирование SOAP-запроса через HTTPСоединение

Перейдем к практическому примеру реализации SOAP-запроса вручную. Мы полностью контролируем отправляемый XML-пакет. Мы можем скопировать эталонную структуру запроса из SOAP UI, подставить туда наши параметры авторизации и отправить серверу.

Проанализируем пошаговый алгоритм отправки SOAP-запроса:


Функция ВыполнитьPingЗапросВручную() Экспорт
    
    // 1. Сформируем XML-тело запроса (SOAP Envelope).
    // Мы берем структуру XML непосредственно из работающего шаблона SOAP UI
    // и динамически подставляем нужные значения параметров.
    
    Логин = "test_test_api";
    Пароль = "0Vqmm1q";
    КлючКлиента = "zdcobyklj280bkj37sz3u5p29p59";
    ТекстСообщения = "TestPing";
    
    ШаблонSOAP = 
    "<soapenv:Envelope xmlns:soapenv=""http://schemas.xmlsoap.org/soap/envelope/"" xmlns:soap=""soap.bronevik"">
    |   <soapenv:Header/>
    |   <soapenv:Body>
    |      <soap:ping>
    |         <soap:PingRequest>
    |            <soap:credentials>
    |               <soap:clientKey>%1</soap:clientKey>
    |               <soap:login>%2</soap:login>
    |               <soap:password>%3</soap:password>
    |            </soap:credentials>
    |            <soap:language>ru</soap:language>
    |            <soap:data>%4</soap:data>
    |         </soap:PingRequest>
    |      </soap:ping>
    |   </soapenv:Body>
    |</soapenv:Envelope>";
    
    ТелоЗапросаXML = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ШаблонSOAP, КлючКлиента, Логин, Пароль, ТекстСообщения);
    
    // 2. Настраиваем параметры подключения к веб-серверу
    СерверХост = "api.bronevik.com"; // Используем доменное имя вместо IP-адреса!
    АдресРесурса = "/v2.1.0/api.php";
    
    // Настраиваем SSL-соединение для защищенного канала HTTPS
    БезопасноеСоединение = Новый ЗащищенноеСоединениеOpenSSL();
    
    HTTPСоединение = Новый HTTPСоединение(СерверХост, 443, , , , 30, БезопасноеСоединение);
    
    // 3. Формируем заголовки HTTP-запроса
    HTTPЗапрос = Новый HTTPЗапрос(АдресРесурса);
    HTTPЗапрос.Заголовки.Вставить("Content-Type", "text/xml; charset=utf-8");
    
    // Заголовок SOAPAction обязателен для многих SOAP-серверов. 
    // Его точное значение можно подсмотреть в свойствах запроса в SOAP UI
    HTTPЗапрос.Заголовки.Вставить("SOAPAction", "soap.bronevik#ping"); 
    
    // Устанавливаем подготовленное XML-тело в тело запроса
    HTTPЗапрос.УстановитьТелоИзСтроки(ТелоЗапросаXML, КодировкаТекста.UTF8, ИспользованиеМаркераПоследовательностиByteOrderMark.НеИспользовать);
    
    // 4. Отправляем запрос на сервер
    Попытка
        HTTPОтвет = HTTPСоединение.ОтправитьДляОбработки(HTTPЗапрос);
        КодCостояния = HTTPОтвет.КодСостояния;
        ТекстОтвета  = HTTPОтвет.ПолучитьТелоКакСтроку(КодировкаТекста.UTF8);
    Исключение
        ЗаписьЖурналаРегистрации("Интеграция.Броневик", УровеньЖурналаРегистрации.Ошибка, , , ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
        ВызватьИсключение "Ошибка сетевого обмена с сервером API Броневик.";
    КонецПопытки;
    
    // 5. Обрабатываем полученный ответ
    Если КодCостояния = 200 Тогда
        // Запрос успешно выполнен. Разберем XML-ответ с помощью ЧтенияXML
        ЧтениеXML = Новый ЧтениеXML;
        ЧтениеXML.УстановитьСтроку(ТекстОтвета);
        
        // Здесь мы можем распарсить XML программно или прочитать его элементы
        // Для примера выведем полученный ответ в журнал регистрации
        ЗаписьЖурналаРегистрации("Интеграция.Броневик", УровеньЖурналаРегистрации.Информация, , , "Успешный ответ: " + ТекстОтвета);
        Возврат ТекстОтвета;
    Иначе
        // Сервер вернул ошибку (например, 500 Internal Server Error при неверной структуре SOAP)
        ЗаписьЖурналаРегистрации("Интеграция.Броневик", УровеньЖурналаРегистрации.Предупреждение, , , 
            СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку("Ошибка сервера. Код состояния: %1. Текст ответа: %2", КодCостояния, ТекстОтвета));
        ВызватьИсключение "Не удалось выполнить операцию. Ошибка сервера: " + КодCостояния;
    КонецЕсли;
    
КонецФункции

Использование ручной отправки через HTTPСоединение полностью снимает зависимость от корректности генерации XML-пакетов платформенными механизмами XDTO. Вы отправляете ровно ту структуру данных, которую подготовили, что гарантирует идентичность работы запросов в вашей 1С и в утилите SOAP UI.

← На главную