Универсальный обмен между идентичными конфигурациями через 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 не будет опубликован. Обязательные поля помечены *