Как вызвать веб-сервис 1С без пакета XDTO и победить ошибку WSDLException

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

При интеграции различных информационных систем на платформе 1С:Предприятие разработчики часто сталкиваются с веб-сервисами (SOAP). Одной из самых распространенных и неприятных проблем при импорте или вызове стороннего SOAP-сервиса является ошибка WSDLException (at /html): faultCode=INVALID_WSDL: Expected element '{http://schemas.xmlsoap.org/wsdl/}definitions'. Дополнительно разработчики часто задаются вопросом: обязательно ли создавать и привязывать собственный пакет XDTO для работы веб-сервиса, если параметры и возвращаемые значения представляют собой простые типы данных?

В этой статье мы подробно разберем природу возникновения ошибки WSDLException, выясним, почему веб-сервер возвращает HTML вместо корректного XML-описания, а также докажем, что веб-сервисы в 1С могут полноценно функционировать без использования пользовательских пакетов XDTO. Мы рассмотрим детальные пошаговые сценарии обхода этой проблемы как на стороне сервера, так и на стороне клиента.

1. Почему возникает ошибка «WSDLException (at /html)... Expected element definitions»?

Для того чтобы понять суть проблемы, проанализируем механизм работы платформы 1С при создании объекта WSПрокси. Когда в коде вызывается конструктор для получения динамического прокси-класса на основании удаленного WSDL-описания, платформа отправляет GET-запрос по указанному адресу. Она ожидает получить строго валидный XML-документ, корневым элементом которого является <wsdl:definitions>.

Если в ответ на этот запрос веб-сервер (IIS или Apache) присылает стандартную HTML-страницу, XML-парсер платформы 1С моментально выдает ошибку, так как первым тегом в полученном файле оказывается <html>. Код faultCode=INVALID_WSDL прямо говорит о том, что полученный файл не соответствует стандарту WSDL.

Связь этой ошибки с HTTP 500 Internal Server Error очевидна: при возникновении критического сбоя на стороне веб-сервера или в процессе инициализации сеанса 1С сервер формирует HTML-страницу с описанием ошибки 500 и отправляет ее клиенту вместо ожидаемого XML. Рассмотрим наиболее частые причины этого сбоя на стороне сервера:

  1. Проблемы с правами доступа к каталогам платформы. Системный пользователь, от имени которого запущен пул приложений веб-сервера (например, IUSR или IIS_IUSRS в Windows, либо www-data в Linux), должен иметь полные права на чтение и выполнение из каталога установки платформы 1С (папка bin), а также права на чтение файла публикации default.vrd.
  2. Конфликт разрядности (для Microsoft IIS). Если на сервере установлена 64-разрядная версия IIS, а публикация выполнена с использованием 32-разрядного модуля расширения веб-сервера 1С (или наоборот), то при попытке обращения к сервису будет происходить сбой. В настройках пула приложений IIS необходимо проверить параметр «Разрешены 32-разрядные приложения» (Enable 32-Bit Applications).
  3. Ошибки инициализации в коде 1С. Когда веб-сервер обрабатывает запрос к WSDL, он пытается инициализировать фоновый сеанс в базе данных 1С. Если в модуле управляемого приложения, модуле обычного приложения или модуле внешнего соединения в процедурах ПриНачалеРаботыСистемы или УстановкаПараметровСеанса написан код, который пытается открыть какую-либо форму, вызывает интерактивные диалоги или некорректно инициализирует параметры сеанса без проверки типа клиента, сервер 1С аварийно завершит этот сеанс, выдав ошибку 500.
  4. Отсутствие свободных лицензий. Для каждого обращения к веб-сервису требуется свободная клиентская лицензия 1С (если в файле default.vrd не настроен пул соединений). При нехватке лицензий попытка инициализации сеанса провалится.

2. Работа веб-сервиса 1С без пакета XDTO

Отвечая на принципиальный вопрос автора темы: да, веб-сервис в 1С может полноценно работать без указания собственного пакета XDTO. Назначение пакета XDTO в свойствах веб-сервиса необходимо только тогда, когда вам требуется передавать сложные структуры данных (например, контрагента с табличной частью адресов, документы со специфической иерархией и т.д.).

Выделим три основных подхода к проектированию веб-сервисов без создания уникальных пакетов XDTO:

  1. Использование стандартных типов XML Schema. Если параметрами вашего метода являются стандартные типы, такие как строка (string), число (int, decimal), дата (dateTime) или булево (boolean), вы можете использовать предопределенные типы из пространства имен XML Schema (обычно это http://www.w3.org/2001/XMLSchema). Эти типы поддерживаются платформой 1С "из коробки" и не требуют никаких дополнительных настроек.
  2. Использование системных типов платформы 1С. Платформа автоматически предоставляет доступ к типам базовой фабрики XDTO, описывающим стандартные коллекции 1С. Например, пространство имен http://v8.1c.ru/8.1/data/core позволяет передавать такие структуры данных, как ValueStorage (ХранилищеЗначений), Structure (Структура), Map (Соответствие) или ValueTable (ТаблицаЗначений).
  3. Концепция «Универсальный транспорт» (JSON / XML в строке). Это один из наиболее популярных подходов в современной разработке на 1С. Вместо того чтобы описывать сложные структуры типов данных в конфигураторе через пакеты XDTO, разработчики создают веб-сервис всего с одним входным параметром типа string и возвращаемым значением типа string. Внутри этого строкового параметра передается сериализованный JSON или XML-документ.

Проанализируем преимущества и недостатки подхода «Универсальный транспорт»:

3. Практические решения проблемы на стороне клиента

Если у вас нет прямого доступа к серверу поставщика услуги для исправления ошибки HTTP 500, но настроить интеграцию жизненно необходимо, рассмотрим два обходных пути, которые можно реализовать полностью на стороне клиента.

Вариант А: Использование локального статического файла WSDL

Этот метод применим, если поставщик сервиса может хотя бы один раз выгрузить корректный файл WSDL-описания (например, через Конфигуратор с помощью команды «Сохранить WSDL в файл») и передать его вам. В этом случае мы уходим от необходимости динамического запроса WSDL с проблемного веб-сервера при каждом создании прокси.

Разберем по шагам, как настроить это решение программно в вашей базе 1С:

  1. Сохраните полученный файл WSDL на локальный диск или сетевой ресурс, к которому имеет доступ сервер 1С (например, C:\WSDL\SupplierService.wsdl).
  2. Инициализируйте объект WSОпределения, указав путь к локальному файлу, а не удаленный URL.
  3. Создайте объект WSПрокси, переопределив в параметрах адрес подключения на реальный работающий сервер публикации.

Рассмотрим пример кода на языке 1С:


// Определяем параметры подключения и пути
ПутьКЛокальномуWSDL = "C:\WSDL\SupplierService.wsdl";
АдресВебСервиса     = "http://srv-pub.supplier.ru/trade/ws/Exchange.1cws";
ПространствоИмен    = "http://www.supplier.ru/exchange";
ИмяСервиса          = "Exchange";
ИмяТочкиПодключения = "ExchangeSoap";

Пользователь        = "ExchangeUser";
Пароль              = "SecurePassword123";

Попытка
    // Инициализируем WSОпределения на основе локального статического файла
    Определения = Новый WSОпределения(ПутьКЛокальномуWSDL, Пользователь, Пароль);
    
    // Создаем прокси-объект
    Прокси = Новый WSПрокси(Определения, ПространствоИмен, ИмяСервиса, ИмяТочкиПодключения);
    
    // Обязательно переопределяем адрес точки подключения на реальный URL
    // (так как в локальном файле может быть прописан неактуальный или внутренний адрес разработчика)
    Прокси.ПолучитьТочкуПодключения().Адрес = АдресВебСервиса;
    
    // Передаем параметры авторизации для выполнения запросов
    Прокси.Пользователь = Пользователь;
    Прокси.Пароль = Пароль;
    
    // Вызываем целевой метод веб-сервиса
    ВходнаяСтрока = "Тестовые данные для отправки";
    РезультатВыполнения = Прокси.ВыполнитьОперацию(ВходнаяСтрока);
    
    Если РезультатВыполнения Тогда
        Сообщить("Запрос успешно обработан веб-сервисом!");
    Иначе
        Сообщить("Сервер вернул отрицательный результат (Ложь).");
    КонецЕсли;
    
Исключение
    ЗаписьЖурналаРегистрации("Интеграция.WS", 
        УровеньЖурналаРегистрации.Ошибка, , , 
        "Ошибка при вызове веб-сервиса через локальный WSDL: " + КраткоеПредставлениеОшибки(ИнформацияОбОшибке()));
    ВызватьИсключение "Не удалось выполнить интеграционную операцию!";
КонецПопытки;

Вариант Б: Формирование ручного SOAP-запроса через HTTP (без WSПрокси)

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

Мы можем собрать тело SOAP-конверта вручную в виде XML-строки и отправить его на сервер с помощью стандартного объекта HTTPСоединение. Этот подход исключает этап разбора схемы WSDL на клиенте.

Рассмотрим подробный пример реализации ручной сборки и отправки SOAP-запроса:


// Настройки подключения к серверу
СерверРесурса = "srv-pub.supplier.ru";
ПортРесурса   = 80;
ПутьКПубликации = "/trade/ws/Exchange.1cws";

Пользователь  = "ExchangeUser";
Пароль        = "SecurePassword123";

// 1. Формируем тело XML-запроса (SOAP Envelope) вручную
ЗаписьXML = Новый ЗаписьXML;
ЗаписьXML.УстановитьСтроку("UTF-8");
ЗаписьXML.ЗаписатьОбъявлениеXML();

// Открываем конверт SOAP
ЗаписьXML.ЗаписатьНачалоЭлемента("soapenv:Envelope");
ЗаписьXML.ЗаписатьСоответствиеПространстваИмен("soapenv", "http://schemas.xmlsoap.org/soap/envelope/");
ЗаписьXML.ЗаписатьСоответствиеПространстваИмен("exch", "http://www.supplier.ru/exchange");

// Пустой блок заголовков Header (требуется стандартом SOAP)
ЗаписьXML.ЗаписатьНачалоЭлемента("soapenv:Header");
ЗаписьXML.ЗаписатьКонецЭлемента(); // Header

// Блок Body, содержащий имя вызываемого метода и параметры
ЗаписьXML.ЗаписатьНачалоЭлемента("soapenv:Body");
ЗаписьXML.ЗаписатьНачалоЭлемента("exch:ВыполнитьОперацию"); // Имя метода

ЗаписьXML.ЗаписатьНачалоЭлемента("exch:ВходнаяСтрока"); // Имя входного параметра
ЗаписьXML.ЗаписатьТекст("Передаваемое значение строки");
ЗаписьXML.ЗаписатьКонецЭлемента(); // ВходнаяСтрока

ЗаписьXML.ЗаписатьКонецЭлемента(); // ВыполнитьОперацию
ЗаписьXML.ЗаписатьКонецЭлемента(); // Body
ЗаписьXML.ЗаписатьКонецЭлемента(); // Envelope

ТелоЗапросаXML = ЗаписьXML.Закрыть();

// 2. Отправляем сформированный SOAP-конверт через HTTP-соединение
Попытка
    HTTPСоединение = Новый HTTPСоединение(СерверРесурса, ПортРесурса, Пользователь, Пароль);
    
    HTTPЗапрос = Новый HTTPЗапрос(ПутьКПубликации);
    
    // Устанавливаем необходимые HTTP-заголовки
    HTTPЗапрос.Заголовки.Вставить("Content-Type", "text/xml; charset=utf-8");
    // Заголовок SOAPAction обязателен для SOAP 1.1 (может быть пустым или содержать URI метода)
    HTTPЗапрос.Заголовки.Вставить("SOAPAction", "http://www.supplier.ru/exchange#Exchange:ВыполнитьОперацию");
    
    // Загружаем тело запроса
    HTTPЗапрос.УстановитьТелоИзСтроки(ТелоЗапросаXML, КодировкаТекста.UTF8, ИспользованиеByteOrderMark.НеИспользовать);
    
    // Отправляем POST-запрос
    HTTPОтвет = HTTPСоединение.ОтправитьДляОбъекта(HTTPЗапрос);
    
    // Анализируем полученный ответ
    Если HTTPОтвет.КодСостояния = 200 Тогда
        СтрокаОтветаXML = HTTPОтвет.ПолучитьТелоКакСтроку(КодировкаТекста.UTF8);
        
        // Читаем полученный XML и извлекаем булево значение
        ЧтениеXML = Новый ЧтениеXML;
        ЧтениеXML.УстановитьСтроку(СтрокаОтветаXML);
        
        РезультатБулево = Неопределено;
        Пока ЧтениеXML.Прочитать() Цикл
            // Ищем узел с возвращаемым значением метода
            Если ЧтениеXML.ТипУзла = ТипУзлаXML.НачалоЭлемента И ЧтениеXML.ЛокальноеИмя = "return" Тогда
                ЧтениеXML.Прочитать(); // Переходим к текстовому содержимому узла
                Если ЧтениеXML.ТипУзла = ТипУзлаXML.Текст Тогда
                    ЗначениеТекста = ВРег(СокрЛП(ЧтениеXML.Значение));
                    РезультатБулево = (ЗначениеТекста = "TRUE" Или ЗначениеТекста = "1" Или ЗначениеТекста = "ИСТИНА");
                КонецЕсли;
                Прервать;
            КонецЕсли;
        КонецЦикла;
        
        Если РезультатБулево = Неопределено Тогда
            Сообщить("Не удалось извлечь результат из ответа сервера.", СтатусСообщения.Важное);
        Иначе
            Сообщить("Успешный ручной вызов! Результат выполнения: " + РезультатБулево);
        КонецЕсли;
    Иначе
        Сообщить("Ошибка вызова веб-сервиса! Код состояния сервера: " + HTTPОтвет.КодСостояния, СтатусСообщения.Важное);
        Сообщить("Текст ошибки от сервера: " + HTTPОтвет.ПолучитьТелоКакСтроку(КодировкаТекста.UTF8));
    КонецЕсли;
    
Исключение
    Сообщить("Сбой сетевого соединения при отправке SOAP-запроса: " + ОписаниеОшибки(), СтатусСообщения.Важное);
КонецПопытки;

4. Резюме и рекомендации

Проанализировав все технические аспекты, подведем итог нашей совместной работы по разбору данной проблемы:

  1. Ошибка WSDLException (at /html) однозначно указывает на то, что веб-сервер при попытке отдать WSDL-схему столкнулся с внутренней ошибкой (HTTP 500) и вернул HTML-страницу со сведениями об ошибке вместо XML. Решать эту проблему необходимо в первую очередь со стороны веб-сервера (проверка прав доступа, проверка разрядности пулов приложений IIS и проверка кода модулей 1С на запуск интерактивных элементов).
  2. Для простых типов данных (строка, булево, числа) указывать собственный пакет XDTO в настройках веб-сервиса 1С не требуется. Платформа прекрасно справляется со стандартными типами данных по умолчанию.
  3. Если у вас нет доступа к серверу публикации для исправления ошибки HTTP 500, вы можете настроить работу через локальный WSDL-файл (Вариант А) или полностью отказаться от WSПрокси в пользу формирования прямых POST-запросов (Вариант Б). Это позволит обойти динамическую генерацию схемы WSDL сервером.
← На главную