Как исправить ошибку «Extra content at the end of the document» при вызове SOAP веб-сервиса в 1С

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

Интеграция систем 1С с внешними веб-сервисами по протоколу SOAP с использованием статических или динамических WSDL-определений — стандартная задача для современного разработчика. Однако в процессе реализации обмена можно столкнуться с трудночитаемыми системными ошибками. Одной из самых распространенных и коварных является ошибка: «При вызове веб-сервиса произошла ошибка. Неизвестная ошибка. Ошибка разбора XML: - [1,1] Фатальная ошибка: Extra content at the end of the document».

В этой статье мы подробно разберем, почему возникает эта ошибка, как специфика импорта WSDL-схем в 1С влияет на передачу параметров, как правильно использовать инструменты отладки для локализации проблемы, и как написать надежный и отказоустойчивый код для работы с SOAP-сервисами.

Выясняем истинную причину ошибки «Extra content...»

Для начала проанализируем, что именно пытается сказать нам платформа 1С. Ошибка разбора XML с указанием позиции [1,1] (первая строка, первый символ) и текстом Extra content at the end of the document (лишнее содержимое в конце документа) означает, что встроенный парсер XML ожидал получить валидный SOAP-конверт, оформленный по всем правилам XML-стандарта, но вместо этого получил некорректные данные.

Почему так происходит? Когда 1С отправляет на сторонний веб-сервис некорректно сформированный SOAP-запрос, удаленный веб-сервер (например, IIS, Kestrel или Apache) не может его обработать. Вместо генерации стандартного XML-ответа с описанием ошибки (SOAP Fault), сервер возвращает стандартную HTTP-страницу с кодом состояния 400 Bad Request или 500 Internal Server Error. Эта страница представляет собой обычный HTML-текст.

Платформа 1С, получив этот ответ, пытается распарсить его как XML. Поскольку HTML-код не соответствует строгому синтаксису XML (например, содержит незакрытые теги, специфичные сущности или лишний текст вне корневого элемента), парсер мгновенно спотыкается на первом же символе и выдает фатальное исключение. Таким образом, истинная причина ошибки разбора XML кроется не в самом ответе, а в том, что мы отправили веб-сервису неверный запрос, который «сломал» логику обработки на стороне сервера.

Особенности импорта WSDL и стиль Document-Literal Wrapped

Рассмотрим подробнее, почему разработчики часто совершают ошибку при формировании параметров SOAP-запроса в 1С. В веб-сервисах, особенно разработанных на платформе .NET (WCF, ASMX), крайне популярен стиль проектирования WSDL, называемый Document-Literal Wrapped.

В этом стиле XML-схема описывает операцию как единый комплексный тип (обертку — wrapper), который содержит внутри себя фактические входные параметры. При импорте WSDL в 1С (через объект WSОпределения или механизм WS-ссылок) платформа выполняет автоматическое «разворачивание» (unwrapping) схемы:

Если же разработчик пытается создать объект операции через Прокси.ФабрикаXDTO.Создать() и передать его целиком, структура SOAP-конверта нарушается. Вместо ожидаемых простых тегов XML-парсер сервиса получает вложенный некорректный объект, что приводит к краху обработки на стороне сервера.

Анализ неверного кода

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


// Создаем определение веб-сервиса
WSОпределение = Новый WSОпределения(НашВебСервис);
WSСервис = WSОпределение.Сервисы[0];
Прокси = Новый WSПрокси(WSОпределение, WSСервис.URIПространстваИмен, WSСервис.Имя, WSСервис.ТочкиПодключения[0].Имя);

// ПОПЫТКА СОЗДАТЬ ОБЕРТКУ ВРУЧНУЮ (ОШИБКА!)
ТипWSПараметра = Прокси.ФабрикаXDTO.Пакеты.Получить((WSСервис.URIПространстваИмен)).Получить("ServisesWithoutSupplier");
WSПараметр = Прокси.ФабрикаXDTO.Создать(ТипWSПараметра);
WSПараметр.partnerID = НашКодПартнера;

serviceList = "";
Error = "";

// Вызов метода с передачей сложного объекта XDTO вместо плоских параметров
Прокси.ServisesWithoutSupplier(WSПараметр, serviceList, Error);

В данном случае метод ServisesWithoutSupplier ожидает на вход три параметра: строковое значение partnerID и две выходные переменные (serviceList и Error). Передача объекта WSПараметр целиком ломает сигнатуру вызова, генерируя неверный SOAP-запрос.

Исправленный и правильный вариант вызова

Разберем по шагам, как написать код правильно. Нам не нужно обращаться к фабрике XDTO для создания входного параметра операции. Мы передаем строковое значение кода партнера напрямую в качестве первого аргумента метода:


// Инициализируем WS-определение и прокси
WSОпределение = Новый WSОпределения(НашВебСервис);
WSСервис = WSОпределение.Сервисы[0];
Прокси = Новый WSПрокси(WSОпределение, WSСервис.URIПространстваИмен, WSСервис.Имя, WSСервис.ТочкиПодключения[0].Имя);

// Подготавливаем переменные для выходных параметров
ВыходнойСписокУслуг = Неопределено;
ВыходнаяОшибка = Неопределено;

// Передаем параметры правильно: плоские входные значения и переменные для результатов
Прокси.ServisesWithoutSupplier(НашКодПартнера, ВыходнойСписокУслуг, ВыходнаяОшибка);

Этот лаконичный вариант работает корректно, поскольку платформа 1С самостоятельно упаковывает переданную строку НашКодПартнера в тег <partnerID> внутри SOAP-тела <ServisesWithoutSupplier>, в полном соответствии с требованиями WSDL-схемы.

Как отлаживать SOAP-запросы с помощью SoapUI и Fiddler

Если вы столкнулись со сложным веб-сервисом и стандартный вызов не работает, незаменимыми инструментами становятся SoapUI и Fiddler. Проанализируем алгоритм работы с ними:

  1. Проверка через SoapUI: Импортируйте WSDL-ссылку в SoapUI. Создайте тестовый запрос для метода ServisesWithoutSupplier. Замените шаблонные значения реальными данными и отправьте запрос. Убедитесь, что сервер возвращает корректный XML-ответ, содержащий элементы <ServiceInfo>. Если SoapUI возвращает ошибку, проблема на стороне самого веб-сервиса или в некорректности переданных данных.
  2. Перехват трафика через Fiddler: Для того чтобы увидеть, какой именно XML-пакет отправляет платформа 1С, настройте проксирование трафика. Включите в настройках Fiddler опцию Allow remote computers to connect и посмотрите порт прокси (по умолчанию 8888).
  3. В коде 1С перед созданием WSПрокси укажите использование прокси-сервера Fiddler:
    
    ПроксиСервер = Новый ИнтернетПрокси;
    ПроксиСервер.Установить("http", "127.0.0.1", 8888);
    // Для WSПрокси задаем прокси-сервер при динамическом вызове через свойства системы
    
    Сравнивая "сырой" XML-текст запроса из SoapUI и XML-текст из Fiddler, отправленный из 1С, вы мгновенно обнаружите различия в пространствах имен (namespaces) или структуре тегов.

Подводный камень: Безопасный обход коллекций XDTO

После успешного вызова метода вы получите ответ в переменной ВыходнойСписокУслуг. Часто разработчики предлагают использовать стандартный обход коллекции следующим образом:


Для Каждого Стр ИЗ ВыходнойСписокУслуг.ServiceInfo Цикл
    // Обработка данных
КонецЦикла;

Внимание! Этот код таит в себе скрытую угрозу, которая обязательно проявит себя в рабочей базе. Дело в особенностях сериализации XDTO-пакетов в платформе 1С:

Для предотвращения подобных ситуаций необходимо использовать защищенный метод ПолучитьСписок(). Разберем безопасный вариант обхода коллекции:


Если ВыходнойСписокУслуг <> Неопределено Тогда
    
    // Получаем гарантированную коллекцию элементов XDTO, независимо от их количества в ответе
    СписокЭлементов = ВыходнойСписокУслуг.ПолучитьСписок("ServiceInfo");
    
    Для Каждого ТекущаяУслуга Из СписокЭлементов Цикл
        ИдентификаторУслуги = ТекущаяУслуга.SerID; // Тип: Число
        НаименованиеУслуги  = ТекущаяУслуга.SerName; // Тип: Строка
        
        // Выполняем полезную работу
        Сообщить("Получена услуга: " + НаименованиеУслуги + " (ID: " + ИдентификаторУслуги + ")");
    КонецЦикла;
    
КонецЕсли;

Применение метода ПолучитьСписок("ИмяСвойства") гарантирует, что платформа всегда вернет итерируемый объект типа СписокXDTO (даже если там всего один элемент или список пуст), что делает ваше интеграционное решение полностью надежным и стабильным.

← На главную