Как передать параметры даты и структуры из одной базы 1С в другую через веб-сервис?

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

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

Описание проблемы

Представим типовую ситуацию: у нас есть база-источник, которая опубликована как веб-сервис. В этой базе определена операция, например, SubmitQuery, которая принимает несколько параметров: TypeOfExchange (предполагается строковый тип, внутри которого содержится сериализованная структура), BeginningDate и EndDate (оба типа Date). В другой базе, базе-приемнике, разработан обработчик, который подключается к веб-сервису базы-источника и вызывает эту операцию для получения данных. На форме обработчика в базе-приемнике пользователь выбирает период, и наша задача — передать выбранные даты (BeginningDate и EndDate), а также другие необходимые параметры, в операцию SubmitQuery веб-сервиса.

Давайте посмотрим на фрагменты кода, чтобы лучше понять контекст:

Код базы-источника (модуль веб-сервиса):


Функция SubmitQuery(TypeOfExchange,BeginningDate,EndDate)
 
    Структура = Десериализовать(TypeOfExchange); // Предполагается JSON/XML строка
    ТаблицаЗначений = Новый Структура; // Инициализация, далее, вероятно, заполняется
    Запрос = Новый Запрос;
    Запрос.УстановитьПараметр("НачалоПериода", BeginningDate);
    Запрос.УстановитьПараметр("КонецПериода", КонецДня(EndDate));
    Запрос.Текст = // Здесь должен быть текст запроса
    // ... дальнейшая обработка запроса и возврат данных
КонецФункции

Код обработчика базы-приемника:


&НаСервере
Функция ВыполнитьЗагрузкуНаСервере()
 
    ТабДок = Новый ТабличныйДокумент;
    // ... инициализация ТабДок и макетов ...
 
    // Предполагается, что НачалоПериода и КонецПериода - реквизиты формы обработчика
    Шапка.Параметры.НачПериод = Формат(НачалоПериода,"ДФ=dd.MM.yyyy");
    Шапка.Параметры.КонПериод = Формат(КонецПериода,"ДФ=dd.MM.yyyy");
 
    ТабДок.Вывести(Шапка);
 
    ТЗ = Новый ТаблицаЗначений;
    ТЗ.Колонки.Добавить("Данные", Новый ОписаниеТипов("Строка",,,, Новый КвалификаторыСтроки(150))); 
 
    Соединение = WSСсылки.ПолучитьДанныеДокументов.СоздатьWSПрокси("http://localhost/ws/CompareDocument", "WebCompareDocument", "WebCompareDocumentSoap");
    Соединение.Пользователь = "Admin";
    Соединение.Пароль = "13788";
 
    // Здесь требуется вызвать операцию SubmitQuery и передать параметры
    // ...
 
    Данные = Новый Структура;
    Данные.Вставить("ТаблицаДанных", ТЗ);
    // ...
КонецФункции

Первоначальная попытка и сложности

Первая мысль при передаче параметров — это прямой вызов метода веб-сервиса через WS-прокси, передавая параметры в том порядке и с теми типами, которые объявлены в определении веб-сервиса (WSDL). Например, таким образом:


// ...
// В базе-приемнике
TypeOfExchangeДляПередачи = ""; // Допустим, это будет JSON-строка или внутренняя строка
НачалоПериодаВыборки = НачалоПериода; // Реквизит формы типа Дата
КонецПериодаВыборки = КонецПериода;   // Реквизит формы типа Дата

// Вызов операции веб-сервиса
Результат = Соединение.SubmitQuery(TypeOfExchangeДляПередачи, НачалоПериодаВыборки, КонецПериодаВыборки);
// ...

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

Окончательное решение: Использование ЗначениеВСтрокуВнутр() и ЗначениеИзСтрокиВнутр()

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

Принцип работы:

  1. В базе-приемнике, перед вызовом веб-сервиса, мы преобразуем сложные параметры (например, структуру или даже даты, если с ними возникают проблемы прямого преобразования) в строковое представление с помощью функции ЗначениеВСтрокуВнутр().
  2. Веб-сервис в базе-источнике получает эти параметры как строки. Затем внутри функции веб-сервиса мы преобразуем эти строки обратно в исходные объекты 1С с помощью функции ЗначениеИзСтрокиВнутр().

Это позволяет обойти потенциальные проблемы с несоответствием типов, так как веб-сервис передает строковые данные, а за преобразование отвечает сама платформа 1С.

Детали реализации в базе-приемнике

Чтобы успешно передать параметры, внесем изменения в код обработчика базы-приемника:

  1. Подготовим структуру для параметра TypeOfExchange: Создадим структуру 1С, которую хотим передать. Затем сериализуем ее в строку с помощью ЗначениеВСтрокуВнутр().
  2. Подготовим параметры дат: Если прямое использование параметров типа Дата вызывает ошибки, их также можно преобразовать в строку с помощью ЗначениеВСтрокуВнутр(). Однако, если веб-сервис корректно обрабатывает тип Date, то лучше передавать их напрямую. В данном случае, автор указал, что ему помогла общая отправка через эти функции, что может говорить о проблемах с типами и для дат. Мы рассмотрим вариант с прямой передачей дат, так как это более корректно, если WSDL их объявляет как даты.
  3. Вызовем операцию веб-сервиса: Используем WS-прокси для вызова метода SubmitQuery, передавая подготовленные параметры.

Пример кода базы-приемника с учетом решения:


&НаСервере
Функция ВыполнитьЗагрузкуНаСервере()
 
    // ... Инициализация ТабДок, макетов и т.д. ...
 
    Соединение = WSСсылки.ПолучитьДанныеДокументов.СоздатьWSПрокси("http://localhost/ws/CompareDocument", "WebCompareDocument", "WebCompareDocumentSoap");
    Соединение.Пользователь = "Admin";
    Соединение.Пароль = "13788";
 
    // 1. Подготавливаем параметр TypeOfExchange.
    // Если TypeOfExchange ожидается как структура, то сериализуем ее:
    СтруктураДляПередачи = Новый Структура;
    СтруктураДляПередачи.Вставить("Ключ1", "Значение1");
    СтруктураДляПередачи.Вставить("Ключ2", 123);
    
    // Сериализуем структуру во внутреннее строковое представление
    СтрокаTypeOfExchange = ЗначениеВСтрокуВнутр(СтруктураДляПередачи);
 
    // 2. Подготавливаем параметры дат.
    // НачалоПериода и КонецПериода - это реквизиты формы обработчика типа Дата.
    // Если WS-прокси корректно обрабатывает тип Date, то передаем напрямую.
    НачалоПериодаЗагрузки = НачалоПериода; // Передаем реквизит формы напрямую
    КонецПериодаЗагрузки = КонецПериода;   // Передаем реквизит формы напрямую
    
    // В случае если прямая передача дат вызывает ошибку (что маловероятно для стандартных типов XDTO), 
    // можно применить аналогичную сериализацию:
    // СтрокаНачалоПериода = ЗначениеВСтрокуВнутр(НачалоПериода);
    // СтрокаКонецПериода = ЗначениеВСтрокуВнутр(КонецПериода);
    // И тогда в функцию SubmitQuery передавать СтрокаНачалоПериода, СтрокаКонецПериода.
    // Однако, по умолчанию для типа Date это обычно не требуется.
 
    // 3. Вызываем операцию веб-сервиса с подготовленными параметрами
    Попытка
        РезультатСервиса = Соединение.SubmitQuery(СтрокаTypeOfExchange, НачалоПериодаЗагрузки, КонецПериодаЗагрузки);
        // Если использовалась сериализация дат:
        // РезультатСервиса = Соединение.SubmitQuery(СтрокаTypeOfExchange, СтрокаНачалоПериода, СтрокаКонецПериода);

        // Дальнейшая обработка РезультатСервиса
        Если РезультатСервиса <> Неопределено Тогда
            // Например, РезультатСервиса может быть строкой, содержащей сериализованную ТЗ
            ТЗПолученная = ЗначениеИзСтрокиВнутр(РезультатСервиса);
            // ... выводим данные в ТабДок ...
        КонецЕсли;
    Исключение
        Сообщить("Ошибка при вызове веб-сервиса: " + ОписаниеОшибки());
    КонецПопытки;
 
    // ... Завершение функции ...
КонецФункции

Детали реализации в базе-источнике

В базе-источнике нам необходимо изменить функцию веб-сервиса SubmitQuery таким образом, чтобы она десериализовала строковые параметры обратно в исходные типы 1С.

Важно: Изначально в коде базы-источника для TypeOfExchange использовалась функция Десериализовать(). Эта функция предназначена для десериализации JSON или XML строк. Если мы используем ЗначениеВСтрокуВнутр() на стороне приемника, то на стороне источника для десериализации нам потребуется ЗначениеИзСтрокиВнутр(). Это означает, что для корректной работы необходимо согласовать методы сериализации/десериализации между базами. Если автор в итоге использовал ЗначениеВСтрокуВнутр / ЗначениеИзСтрокиВнутр, это подразумевает, что код базы-источника был скорректирован.

Пример измененного кода базы-источника (модуль веб-сервиса):


Функция SubmitQuery(TypeOfExchange,BeginningDate,EndDate)
 
    // Десериализуем параметр TypeOfExchange обратно в структуру 1С
    Попытка
        СтруктураПараметров = ЗначениеИзСтрокиВнутр(TypeOfExchange); 
        // Теперь СтруктураПараметров содержит исходную структуру
        // Можно получить значения, например:
        // ЗначениеКлюча1 = СтруктураПараметров.Ключ1;
    Исключение
        Сообщить("Ошибка десериализации TypeOfExchange: " + ОписаниеОшибки());
        Возврат Неопределено; // Или другая обработка ошибки
    КонецПопытки;

    // Параметры BeginningDate и EndDate, если они передавались как Date,
    // будут доступны напрямую. Если они были сериализованы как строки 
    // с помощью ЗначениеВСтрокуВнутр(), то их также нужно десериализовать:
    // НачалоПериодаЗапроса = ЗначениеИзСтрокиВнутр(BeginningDate);
    // КонецПериодаЗапроса = ЗначениеИзСтрокиВнутр(EndDate);
    // Но при прямом типе Date в WSDL это не нужно.
    // Используем переданные напрямую даты:
    НачалоПериодаЗапроса = BeginningDate;
    КонецПериодаЗапроса = EndDate;
 
    Запрос = Новый Запрос;
    Запрос.УстановитьПараметр("НачалоПериода", НачалоПериодаЗапроса);
    Запрос.УстановитьПараметр("КонецПериода", КонецДня(КонецПериодаЗапроса));
    Запрос.Текст =
        "ВЫБРАТЬ
        |    Справочник.Ссылка КАК Данные
        |ИЗ
        |    Справочник
        |ГДЕ
        |    Справочник.ДатаСоздания МЕЖДУ &НачалоПериода И &КонецПериода"; // Пример текста запроса
    
    // Выполняем запрос
    РезультатЗапроса = Запрос.Выполнить().Выгрузить();
    
    // Если нужно вернуть ТаблицуЗначений, ее также можно сериализовать
    Возврат ЗначениеВСтрокуВнутр(РезультатЗапроса);
    // Или, если веб-сервис поддерживает XDTO-тип для таблицы значений,
    // вернуть ее напрямую, но тогда нужно описывать XDTO-пакеты.

КонецФункции

Дополнительные рекомендации по передаче данных через веб-сервисы 1С

Помимо использования ЗначениеВСтрокуВнутр() / ЗначениеИзСтрокиВнутр(), рассмотрим другие важные аспекты и лучшие практики при работе с веб-сервисами 1С:

  1. Согласование типов XDTO:

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

    Типичные проблемы и их решения:

    • Даты: Если в WSDL дата определена как xs:dateTime или xs:date, 1С обычно корректно передает значения типа Дата. Проблемы могут возникнуть, если на стороне веб-сервиса ожидается строковое представление даты в определенном формате. В таких случаях может потребоваться ручное форматирование даты с помощью функции Формат() в базе-приемнике и последующий парсинг строки на стороне базы-источника.
    • Сложные объекты 1С: Для Структура, ТаблицаЗначений, Массив и других прикладных типов 1С, которые не имеют прямого эквивалента в простых XDTO-типах, часто используется строковое представление. В этом случае, как мы видели, ЗначениеВСтрокуВнутр() / ЗначениеИзСтрокиВнутр() являются эффективным решением для обмена между 1С-системами.
  2. XDTO-сериализация для универсального обмена:

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

    Пример XDTO-сериализации структуры в JSON:

    
    // В базе-приемнике:
    ФабрикаXDTO = Новый ФабрикаXDTO;
    СтруктураДляПередачи = Новый Структура;
    СтруктураДляПередачи.Вставить("Ключ", "Значение");
            
    // Преобразование структуры 1С в объект XDTO
    ОбъектXDTO = ФабрикаXDTO.Создать(ФабрикаXDTO.Тип("http://v8.1c.ru/8.1/data/ui", "Структура")); 
    ЗаполнитьЗначенияСвойств(ОбъектXDTO, СтруктураДляПередачи);
            
    // Сериализация объекта XDTO в JSON-строку
    ЗаписьJSON = Новый ЗаписьJSON;
    ЗаписьJSON.УстановитьСтроку();
    ФабрикаXDTO.ЗаписатьJSON(ЗаписьJSON, ОбъектXDTO);
    СтрокаJSON = ЗаписьJSON.Закрыть();
            
    // Передача СтрокаJSON в веб-сервис
    // Результат = Соединение.SubmitQuery(СтрокаJSON, НачалоПериода, КонецПериода);
    
    
    // В базе-источнике:
    // Структура = Десериализовать(TypeOfExchange); // Это ожидаемый код из примера
    // Где TypeOfExchange - это СтрокаJSON, переданная из базы-приемника.
    // Функция Десериализовать() как раз и работает с JSON/XML строками.
    

    Этот подход более универсален, но требует четкого описания XDTO-типов и пакетов, если мы хотим передавать более сложные прикладные объекты. Для быстрой подготовки схем можно использовать обработку для автоматического формирования XSD-пакета.

  3. Обработка пустых и неопределенных значений:

    Передача значений Null или Неопределено может вызывать ошибки, если веб-сервис ожидает строго определенный тип. Всегда предусматривайте явную обработку таких ситуаций или используйте типы XDTO, которые допускают пустые значения (nillable="true" в XML Schema).

  4. Объем передаваемых данных:

    Для веб-сервисов рекомендуется передавать умеренные объемы данных (до нескольких мегабайт). Если требуется передать очень большие объемы, рассмотрите варианты с передачей данных в виде архивов ZIP или использованием файловых обменов с передачей ссылок через веб-сервис.

  5. Отладка веб-сервисов:

    При возникновении проблем с веб-сервисами, используйте инструменты отладки 1С. Можно настроить автоматическое подключение к веб-сервису, опубликованному на веб-сервере, для пошаговой отладки его кода. Также полезно просматривать логи веб-сервера (Apache, IIS) для выявления низкоуровневых ошибок.

Передача параметров между базами 1С через веб-сервисы — это мощный инструмент интеграции. Правильный выбор методов сериализации и десериализации, а также внимательное отношение к типам данных, являются ключом к успешной и стабильной работе таких систем. В случае обмена между двумя базами 1С, как показал опыт, ЗначениеВСтрокуВнутр() и ЗначениеИзСтрокиВнутр() могут стать простым и эффективным решением для обхода проблем с несоответствием типов.

← На главную