Универсальный обмен между идентичными конфигурациями через REST интерфейс OData. Часть І: Справочники

Сейчас все чаще интеграции различных конфигураций проектируются через HTTP-сервисы — они и работают быстрее, и «войти» в режим отладки гораздо проще, тем самым обойдя «черный ящик» универсального обмена через xml, например.
Более года назад я начал работать в компании, в которой разработчики работали с конфигурациями 1С в режиме совместимости еще 8.2.16 (менять режим совместимости в типичных базах мы не хотели) — а как Вы наверное знаете, если интересовались HTTP-сервисами в 1С, их использование в режиме совместимости 8.3.4 и ниже недопустимо — и здесь я уже не надеялся на разработку и использование HTTP-сервисов.
Но позже меня заинтересовал такой «сервис» как REST интерфейс OData, так как его можно использовать не меняя режим совместимости конфигурации — именно он и стал для меня идеальным вариантом решения «нетривиальных» задач.

 

Итак, начиная с версии платформы 8.3.5 в 1С появился REST интерфейс OData. Подробнее о его реализацию можно почитать на Зазеркалье 1С  и закрытом разделе ИТС.
В двух словах, REST это способ взаимодействия с помощью HTTP запросов. OData это стандарт описывающих формат ЭТИХ запросов и ответов на них.
Для работы с ним достаточно установить флажок "публиковать стандартный интерфейс OData" при публикации конфигурации на web-сервер (смотрите ниже).

Если в конфигурации установлен режим совместимости 8.3.4 или ниже, то возможна публикация только всех метаданных сразу.
Если же у Вас в конфигурации отключен режим совместимости, то вы можете воспользоваться методом УстановитьСоставСтандартногоИнтерфейсаOData () який позволяет установить список публикуемых объектов. Пример:
&НаСервере
Процедура УстановитьODataНаСервере()

тМассив = Новый Массив;
тМассив.Добавить(Метаданные.Справочники.ФизическиеЛица);

УстановитьСоставСтандартногоИнтерфейсаOData(тМассив);

КонецПроцедуры
После ознакомления с данным сервисом (подробнее не буду рассказывать — в интернете уже есть достаточное количество материала для ознакомления), так как в нашей компании более сотни фронт-офисных узлов, было принято решение собирать данные с узлов используя интерфейс OData — сначала данные тех объектов "публичность" которых мы установили выше используя УстановитьСоставСтандартногоИнтерфейсаOData(тМассив).
В результате получился общий модуль — для передачи (создания / обновления) данных на сервере сбора данных из узла.
 
Для начала мы добавили константу СерверСбораДанныхДляЗарплатнойСистемы (Строка, 30), в которой указывали адрес публикации сервера сбора данных (например, zbor_centr).
Далее добавили код "отправки" данных на сервер в процедуры "ПриЗаписи" Справочника и "ОбработкаПроведения" документа.
В модуле объекта справочника в процедуре "ПриЗаписи" он выглядит так:
Если ОбменССерверомСбораДанных.СоединенияССерверомСбораДанныхУстановлено() Тогда

ОбменССерверомСбораДанных.ОтправитьНаСерверСбораДанных(ЭтотОбъект.Ссылка, , 1);

КонецЕсли; 
Что это такое?:) Сначала о простом — функция общего модуля ОбменССерверомСбораДанных СоединенияССерверомСбораДанныхУстановлено ():
Функция СоединенияССерверомСбораДанныхУстановлено() Экспорт

Возврат (НЕ Константы.СерверСбораДанныхДляЗарплатнойСистемы.Получить() = "");

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

Далее опишу метод отправки пока только справочника, если будет спрос — будет и продолжения статьи (по обмену документов и регистров).

Вот общая процедура отправки данных на сервер — ОтправитьНаСерверСбораДанных ():

 

 Процедура ОтправитьНаСерверСбораДанных(СсылкаИсточник, DELETE = Ложь, ТипОбъекта = 0) Экспорт

Комментарий к ОтправитьНаСерверСбораДанных () — первым параметром передаем ссылку (документа, справочника), второй параметр — пока не используеться (заготовка на будущее — для отправки команд на удаление), и ТипОбъекта — если отправляем справочник — передаем числом 1, документ — 2.
Процедуры создания теневой копий — это функционал, в случае которого, если не удалось доставить данные на сервер — сделать это позже регламентным заданием (здесь вы как разработчик по-своему можете придумать "офлайн" режим)
 
Итак, самое интересное:
1) Функция СправочникДоставленоУспешно (СсылкаСправочник, DELETE, Метод = "POST", Тень = Ложь)
Параметры:
СсылкаСправочник — ссылка справочника
DELETE — булево — удаление на сервере сбора данных по GUID
Метод — строка — по умолчанию — "POST" (о других методах тоже достаточно материалов в свободном доступе)
Тень — булево — если функцию использует регламентное задание, этот параметр будет истина.
Тело функции:
 

 Функция СправочникДоставленоУспешно(СсылкаСправочник, DELETE, Метод = "POST", Тень = Ложь) 

Функция СправочникДоставленоУспешно(СсылкаСправочник, DELETE, Метод = "POST", Тень = Ложь)

Если Константы.СерверСбораДанныхОфлайн.Получить() И (НЕ Тень) Тогда // если связи нету можна просто перейти в офлайн используя  Константы.СерверСбораДанныхОфлайн.Установить(Истина)
Возврат Ложь;
КонецЕсли;

Связь = ПолучитьКаналСвязиССерверомСбораДаних(); // служебное - смотри далее

ИмяСправочника = СсылкаСправочник.Метаданные().Имя;

Если Тень Тогда
// теневые копию я создавал в идентичном обьъкте как оригинальний справочник, только с префиксом «_с_». Зачем? Запрос по теневым копиям работают быстрее, и в случае успешной доставки теневой копию — копия удалялась
ИмяСправочника = СтрЗаменить(ИмяСправочника,"_с_","");
КонецЕсли;

guid =  Строка(СсылкаСправочник.УникальныйИдентификатор());

Если DELETE Тогда // если удалить объект

АдресРесурса =  "/" + Связь.Порт + "/odata/standard.odata/Catalog_" + ИмяСправочника + "(guid'"+ guid + "')";

Соединение = Новый HTTPСоединение(Связь.Сервер);

ЗаголовокHTTP = Новый Соответствие();
ЗаголовокHTTP.Вставить("Accept", "application/atom+xml,application/xml");
ЗаголовокHTTP.Вставить("Accept-Charset", "UTF-8");
ЗаголовокHTTP.Вставить("DELETE" + " /" + Связь.Порт + "/odata/standard.odata/Catalog_" + ИмяСправочника + "(guid'"+ guid + "')");
ЗаголовокHTTP.Вставить("Content-Type", "application/atom+xml");
ЗаголовокHTTP.Вставить("DataServiceVersion", "3.0;NetFx");
ЗаголовокHTTP.Вставить("MaxDataServiceVersion", "3.0;NetFx");
ЗаголовокHTTP.Вставить("User-Agent", "1C-Enterprise");
ЗаголовокHTTP.Вставить("Host", Связь.Сервер);
Запрос = Новый HTTPЗапрос(АдресРесурса, ЗаголовокHTTP);
Ответ = Соединение.ВызватьHTTPМетод("DELETE", Запрос);

Возврат Ответ.КодСостояния = 204 // случае успешного удаления сервер вернет код состояния  =204
Иначе // если создать/обновить объект

АдресРесурса = "/" + Связь.Порт + "/odata/standard.odata/Catalog_" + ИмяСправочника + ?(Метод = "PUT", "(guid'" + guid + "')","");

ТекстЗапроса = ОпределитьШапкуЗапроса(guid); // служебное - смотри далее

// соответствие стандартных реквизитов
CписокCтандартныхРеквизитов = СоздатьОписанияОбязательнихРеквизитовСправочника(СсылкаСправочник); // служебное - смотри далее
Для Каждого ОписаниеРеквизита ИЗ CписокCтандартныхРеквизитов Цикл
ТекстЗапроса = ТекстЗапроса + "
|         <d:" + ОписаниеРеквизита.Ключ + ">" + СокрЛП(ОписаниеРеквизита.Значение) + "</d:" + ОписаниеРеквизита.Ключ + ">";
КонецЦикла;

// соответствие дополнительных реквизитов
СписокДопРеквизитов = СоздатьОписанияДополнительнихРеквизитов(СсылкаСправочник); // служебное - смотри далее
Для Каждого ОписаниеДопРеквизита ИЗ СписокДопРеквізитів Цикл
ТекстЗапроса = ТекстЗапроса + "
|         <d:" + ОписаниеДопРеквизита.Ключ + ">" + СокрЛП(ОписаниеДопРеквизита.Значение) + "</d:" + ОписаниеДопРеквизита.Ключ + ">";
КонецЦикла;

// соответствие табличных частей
ОписаниеТабличныхЧастей = СоздатьОписанияТабличныхЧастей(СсылкаСправочник, "Catalog", Тень); // служебное - смотри далее
Если НЕ ОписаниеТабличныхЧастей = "" Тогда
ТекстЗапроса = ТекстЗапроса + ОписаниеТабличныхЧастей;
КонецЕсли;

ТекстЗапроса = ТекстЗапроса + "
|      </m:properties>
|   </content>
|</entry>";

Соединение = Новый HTTPСоединение(Связь.Сервер);
Заголовки = Новый Соответствие;
Заголовки.Вставить("Accept", "application/atom+xml,application/xml");
Заголовки.Вставить("Accept-Charset", "UTF-8");
Заголовки.Вставить(Метод + "  /" + Связь.Порт + "/odata/standard.odata/Catalog_" + ИмяСправочника + ?(Метод = "PUT", "(guid'" + guid + "')","") + " HTTP/1.1");
Заголовки.Вставить("Content-Type", "application/atom+xml");
Заголовки.Вставить("DataServiceVersion", "3.0;NetFx");
Заголовки.Вставить("MaxDataServiceVersion", "3.0;NetFx");
Заголовки.Вставить("User-Agent", "1C-Enterprise");
Заголовки.Вставить("Host", Связь.Сервер);

Попытка
Запрос = Новый HTTPЗапрос(АдресРесурса, Заголовки);
Запрос.УстановитьТелоИзСтроки(ТекстЗапроса);
Ответ = Соединение.ВызватьHTTPМетод(Метод, Запрос);

Если (Ответ.КодСостояния <> 201) И (Метод = "POST") Тогда // успешным для POST считается код 201
Возврат СправочникДоставленоУспешно(СсылкаСправочник, DELETE,"PUT", Тень);
ИначеЕсли (Ответ.КодСостояния <> 200) И (Метод = "PUT") Тогда // успешным для PUT считается код 200
Возврат Ложь
Иначе
Возврат Истина
КонецЕсли;

Исключение
Возврат Ложь;
КонецПопытки;

КонецЕсли;

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

 

 

И, конечно, список функций обозначенных комментарием "служебное":

 

 Функция ПолучитьКаналСвязиССерверомСбораДаних()

 

 Функция СоздатьОписанияДополнительнихРеквизитов(СсылкаОбъекта, СписокСсылочных = 0)

 

 Функция СоздатьОписанияТабличныхЧастей(СсылкаОбъекта, ПрефиксОбъекта, Тень = Ложь)

 

 Функция НормализироватьКОбмену(ЭтотРеквизит)

 

 Функция ОпределитьШапкуЗапроса(GUID = "00000000-0000-0000-0000-000000000000")

 

 Функция СоздатьОписанияОбязательнихРеквизитовСправочника(СсылкаСправочник)

 

В результате мы получили универсальный модуль создания / обновления / удаления справочников.
 
 
 
 
Спасибо, что дочитали до конца! 🙂

 

 

11 Comments

  1. VasilVtoroy

    Во вступлении к статье ошибка — использовать HTTP-сервисы можно в любом режиме совместимости.

    Ограничение на использование в режиме совместимости было только в версии 8.3.7. Смотрите V8Update для версии 8.3.8

    https://its.1c.ru/db/v838doc#content:121:1:issogl1_5a3feda0-6292-11e5-a3f7-0050569f678a

    Reply
  2. V.Stavinsky

    (1) «Для конфигураций, в которых используются HTTP-сервисы, разрешена установка режима совместимости Версия 8.3.4 и ниже.

    При необходимости работать с конфигурацией, которая содержит HTTP-сервисы, на системе «1С:Предприятие» версии 8.3.8 и выше, но которая открывалась конфигуратором системы «1С:Предприятие» версии 8.3.4, необходимо выполнить следующие действия:

    Создать любой объект конфигурации;

    Выполнить сохранение конфигурации информационной базы;

    Удалить созданный объект конфигурации;

    Выполнить сохранение конфигурации информационной базы.»

    понял) попробуем сделать действия которые там описанные. но главный акцент статьи все таки это Rest OData

    Reply
  3. VarLone

    После выхода продолжения, не забудь те в этой статье вставить ссылку на продолжение

    Reply
  4. dmarenin

    (0) код не читаем(не глагол)

    Reply
  5. V.Stavinsky

    (8) спасибо, будет исправлено!

    Reply
  6. Dorosh

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

    Reply
  7. V.Stavinsky

    (8) если менять пакеты данных — то да; но у нас один элемент справочника — одна «отправка» в другую базу; ответ приходит моментально; и уже в зависимости от ответа — если он отрицательный, то оставляем на «переотправку» фоновому заданию

    Reply
  8. Dorosh

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

    Reply
  9. V.Stavinsky

    (10) да, согласен, пока проблем нет; тогда в базе приемнике будем дописывать «очередь», чтобы избежать блокировок (и тут уже неважно как «направляются» данные туда — или онлайн, или фоново)

    Reply
  10. V.Stavinsky

    да, согласен, пока проблем нет; тогда в базе приемнике будем дописывать «очередь», чтобы избежать блокировок (и тут уже неважно как «направляются» данные туда — или онлайн, или фоново)

    Reply

Leave a Comment

Ваш адрес email не будет опубликован. Обязательные поля помечены *