Как передать меньшее количество параметров в метод веб-сервиса через WS-ссылку и обойти ошибку «Недостаточно фактических параметров»

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

При интеграции 1С:Предприятия со сторонними веб-сервисами (особенно разработанными на языках Java, C# или PHP) разработчики часто сталкиваются со строгими требованиями к структуре XML-пакетов. Типичная проблема возникает, когда метод веб-сервиса содержит необязательные параметры. В схеме WSDL такие элементы помечены свойством minOccurs="0". Это означает, что на уровне XML-документа данные теги могут полностью отсутствовать.

Однако платформа 1С:Предприятие при работе через стандартный объект WSПрокси накладывает жесткое ограничение: сигнатура метода в коде должна строго соответствовать количеству параметров, описанных в WSDL. Если метод принимает 4 параметра, мы обязаны передать в вызов функции ровно 4 аргумента. Попытка опустить необязательный параметр в коде 1С приводит к ошибке компиляции или выполнения: «Недостаточно фактических параметров».

Если же мы передаем в качестве четвертого параметра значение Неопределено, платформа 1С принудительно сериализует его в SOAP-запрос в виде пустого тега (например, <runAs/>) или добавляет атрибут xsi:nil="true". Строгие валидаторы внешних серверов часто не переносят пустые теги или признак nil для строковых типов, возвращая ошибку SERVER_ERROR или выбрасывая внутреннее исключение NullPointerException. Выясним причину такого поведения и разберем по шагам несколько эффективных способов решения этой проблемы.

Почему возникает конфликт между 1С и сторонними веб-сервисами?

Проанализируем ситуацию с технической стороны. По умолчанию 1С импортирует WSDL-описание в режиме document/literal wrapped. Это значит, что плоский список параметров метода в коде 1С при генерации запроса автоматически «оборачивается» в единый XML-конверт. Если параметр имеет свойство minOccurs="0", но не помечен как nillable="true" (возможно пустое), передача Неопределено из 1С приводит к некорректной генерации SOAP-сообщения:

Для решения этой проблемы рассмотрим три практических метода: от локальной корректировки WSDL-схемы до ручного формирования SOAP-конверта.

Решение 1. Локальная модификация WSDL-схемы

Это наиболее элегантный способ, который позволяет продолжить использование стандартного объекта WSПрокси и фабрики XDTO, не внося изменений в конфигурацию 1С и не переписывая интеграцию на HTTP-запросы. Суть метода заключается в том, что мы сохраняем оригинальный файл описания WSDL, правим его под нужды 1С и инициализируем подключение из локального источника.

Выполним процедуру по шагам:

  1. Откроем браузер и перейдем по адресу WSDL-описания веб-сервиса. Сохраним полученную страницу на локальный диск как файл с расширением .wsdl или .xml.
  2. Откроем сохраненный файл в любом текстовом редакторе с подсветкой синтаксиса (например, Notepad++).
  3. Найдем структуру нужного нам метода. В нашем примере это комплексный тип SaveDataObjectRecord. Описание параметров в схеме выглядит следующим образом:
    
    <xsd:complexType name="SaveDataObjectRecord">
     <xsd:sequence>
      <xsd:element name="DataObjectRecord" type="ns0:dataObjectRecord" form="qualified" minOccurs="0"/>
      <xsd:element name="lang" type="xsd:string" form="qualified" minOccurs="0"/>
      <xsd:element name="change_version" type="xsd:boolean" form="qualified"/>
      <xsd:element name="runAs" type="xsd:string" form="qualified" minOccurs="0"/>
     </xsd:sequence>
    </xsd:complexType>
    
  4. Поскольку параметр runAs вызывает ошибку на сервере при передаче пустых значений, а сам поставщик сервиса разрешает вызывать метод без него, мы можем полностью удалить описание этого элемента из локальной схемы. Удаляем строку: <xsd:element name="runAs" type="xsd:string" form="qualified" minOccurs="0"/>
  5. Сохраняем измененный файл. Теперь в нашей локальной схеме метод принимает ровно 3 параметра.
  6. Загружаем измененный WSDL-файл в конфигурацию 1С в качестве макета с типом Двоичные данные (или сохраняем его в доступную для сервера 1С сетевую папку).
  7. Переписываем инициализацию веб-сервиса в коде 1С, указывая путь к локально измененному файлу схемы:

// Получаем путь к локальному файлу WSDL из макета двоичных данных
ПутьКФайлуWSDL = ПолучитьИмяВременногоФайла("wsdl");
МакетWSDL = Справочники.МакетыИнтеграции.ПолучитьМакетWSDL(); // Пример получения макета
МакетWSDL.Записать(ПутьКФайлуWSDL);

// Инициализируем WSОпределения на основе локального скорректированного файла
Определение = Новый WSОпределения(ПутьКФайлуWSDL, Пользователь, Пароль);

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

// Теперь 1С ожидает ровно 3 параметра, и вызов завершится успешно:
Параметр_DataObjectRecord = ПолучитьОбъектDataObjectRecord(Прокси);
Параметр_lang = "RU";
Параметр_change_version = Ложь;

ПолученныеДанные = Прокси.SaveDataObjectRecord(Параметр_DataObjectRecord, Параметр_lang, Параметр_change_version);

При таком подходе платформа 1С сформирует SOAP-запрос, содержащий ровно 3 XML-элемента. Сторонний сервер успешно примет и обработает этот пакет, так как отсутствие четвертого параметра полностью укладывается в его внутреннюю бизнес-логику.

Решение 2. Ручное формирование SOAP-запроса через HTTPСоединение

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

Рассмотрим пример реализации такого подхода по шагам:

  1. Подготовим шаблон SOAP-конверта. Мы уберем из него тег runAs, оставив только необходимые параметры.
  2. Используем объект ЗаписьXML для динамического формирования тела запроса.
  3. Отправим запрос на сервер через HTTPСоединение методом POST.

Посмотрим на пример кода, реализующий данную схему работы:


// Шаг 1. Создаем файл для формирования тела запроса
ИмяФайлаЗапроса = ПолучитьИмяВременногоФайла("xml");
ЗаписьXML = Новый ЗаписьXML;
ЗаписьXML.ОткрытьФайл(ИмяФайлаЗапроса, "UTF-8");
ЗаписьXML.ЗаписатьОбъявлениеXML();

// Шаг 2. Записываем SOAP-конверт вручную
ЗаписьXML.ЗаписатьНачалоЭлемента("soapenv:Envelope");
ЗаписьXML.ЗаписатьАтрибут("xmlns:soapenv", "http://schemas.xmlsoap.org/soap/envelope/");
ЗаписьXML.ЗаписатьАтрибут("xmlns:web", ПространствоИмен);

ЗаписьXML.ЗаписатьНачалоЭлемента("soapenv:Header");
// При необходимости здесь можно разместить заголовки авторизации WS-Security
ЗаписьXML.ЗаписатьКонецЭлемента(); // soapenv:Header

ЗаписьXML.ЗаписатьНачалоЭлемента("soapenv:Body");
ЗаписьXML.ЗаписатьНачалоЭлемента("web:SaveDataObjectRecord");

// Записываем структуру первого комплексного параметра DataObjectRecord
ЗаписьXML.ЗаписатьНачалоЭлемента("web:DataObjectRecord");
// Заполняем свойства объекта DataObjectRecord (в соответствии с требованиями схемы)
ЗаписьXML.ЗаписатьСодержимоеЭлемента("12345", "web:id");
ЗаписьXML.ЗаписатьСодержимоеЭлемента("Тестовая запись", "web:name");
ЗаписьXML.ЗаписатьКонецЭлемента(); // web:DataObjectRecord

// Записываем простые параметры метода
ЗаписьXML.ЗаписатьСодержимоеЭлемента("RU", "web:lang");
ЗаписьXML.ЗаписатьСодержимоеЭлемента("false", "web:change_version");

// Обратите внимание: параметр runAs мы просто игнорируем, тег не создается!

ЗаписьXML.ЗаписатьКонецЭлемента(); // web:SaveDataObjectRecord
ЗаписьXML.ЗаписатьКонецЭлемента(); // soapenv:Body
ЗаписьXML.ЗаписатьКонецЭлемента(); // soapenv:Envelope

ЗаписьXML.Закрыть();

// Шаг 3. Отправляем подготовленный XML-файл через HTTPСоединение
Соединение = Новый HTTPСоединение("srs.gs1ru.org", , Пользователь, Пароль, , , Новый ЗащищенноеСоединениеOpenSSL);

Заголовки = Новый Соответствие;
Заголовки.Вставить("Content-Type", "text/xml; charset=utf-8");
// SOAPAction может быть обязательным заголовком в зависимости от спецификации сервиса
Заголовки.Вставить("SOAPAction", ПространствоИмен + "/SaveDataObjectRecord"); 

Запрос = Новый HTTPЗапрос("/GS46_Interfaces/GS1RU_Operations", Заголовки);
Запрос.УстановитьИмяФайлаТела(ИмяФайлаЗапроса);

Ответ = Соединение.ОтправитьДляОбработки(Запрос);

// Шаг 4. Анализируем ответ сервера
Если Ответ.КодСостояния = 200 Тогда
    ЧтениеXML = Новый ЧтениеXML;
    ЧтениеXML.ОткрытьФайл(Ответ.ПолучитьИмяФайлаТела());
    
    // Читаем и разбираем полученный XML-ответ от сервера
    Пока ЧтениеXML.Прочитать() Цикл
        // Логика разбора ответа
    КонецЦикла;
    ЧтениеXML.Закрыть();
Иначе
    ВызватьИсключение "Ошибка выполнения SOAP-запроса: " + Ответ.ПолучитьТелоКакСтроку();
КонецЕсли;

// Удаляем временный файл запроса
УдалитьФайлы(ИмяФайлаЗапроса);

Данный подход является максимально надежным и полностью изолирует код 1С от любых ограничений со стороны платформенного парсера WSDL-схем. Вы полностью контролируете структуру XML на выходе.

Решение 3. Изменение свойств XDTO-пакета в конфигурации 1С

Если вы используете статическую WS-ссылку, добавленную непосредственно в дерево метаданных конфигурации, вы можете попытаться исправить поведение платформы без изменения программного кода вызова. Для этого проанализируем структуру импортированного XDTO-пакета.

Разберем по шагам порядок настройки свойств элемента схемы:

  1. В окне конфигуратора разверните ветку Общие -> WS-ссылки и откройте свойства вашей проблемной WS-ссылки.
  2. Найдите привязанный к ней XDTO-пакет, в котором описана структура типов веб-сервиса.
  3. Найдите тип объекта SaveDataObjectRecord и выберите входящее в него свойство runAs.
  4. В палитре свойств обратите внимание на следующие параметры:
    • Возможно пустое (Nillable) — если здесь установлено значение Истина, измените его на Ложь. Это предотвратит принудительное добавление платформой атрибута xsi:nil="true", который отторгается сервером назначения.
    • Минимальное количество (MinOccurs) — убедитесь, что здесь установлено значение 0.
  5. Сохраните изменения в конфигурации.

После изменения данных настроек платформа в ряде версий при передаче аргумента Неопределено сможет корректно исключить тег runAs из финальной XML-сериализации, при этом не ругаясь на количество аргументов при вызове метода прокси-класса.

Решение 4. Переход на Document/Literal Bare стиль (передача единого XDTO-объекта)

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

В таком случае вызов метода в коде будет выглядеть следующим образом:


// 1. Получаем тип параметра-обертки из фабрики XDTO
ТипПараметра = Прокси.ФабрикаXDTO.Тип(ПространствоИмен, "SaveDataObjectRecord");

// 2. Создаем сам объект параметров
ОбъектПараметров = Прокси.ФабрикаXDTO.Создать(ТипПараметра);

// 3. Заполняем только нужные свойства
ОбъектПараметров.DataObjectRecord = ПолучитьОбъектDataObjectRecord(Прокси);
ОбъектПараметров.lang = "RU";
ОбъектПараметров.change_version = Ложь;

// Свойство runAs мы оставляем незаполненным. 
// Так как в схеме его minOccurs = 0, платформа 1С просто не добавит этот тег в XML при сериализации!

// 4. Вызываем метод, передавая ему ЕДИНСТВЕННЫЙ параметр-объект
ПолученныеДанные = Прокси.SaveDataObjectRecord(ОбъектПараметров);

Этот способ совмещает в себе удобство работы с типизированными объектами 1С и гибкость формирования результирующего XML-пакета без лишних пустых тегов.

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

← На главную