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

Обмен - Перенос данных из 1C8 в 1C8

82
Сейчас все чаще интеграции различных конфигураций проектируются через 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")
 
 Функция СоздатьОписанияОбязательнихРеквизитовСправочника(СсылкаСправочник)

 

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

 

 
82

См. также

Комментарии
Сортировка: Древо
1. VasilVtoroy 11.05.18 23:11 Сейчас в теме
Во вступлении к статье ошибка - использовать HTTP-сервисы можно в любом режиме совместимости.

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

https://its.1c.ru/db/v838doc#content:121:1:issogl1_5a3feda0-6292-11e5-a3f7-0050569f678a
V.Stavinsky; +1 Ответить
2. V.Stavinsky 183 13.05.18 18:50 Сейчас в теме
(1) "Для конфигураций, в которых используются HTTP-сервисы, разрешена установка режима совместимости Версия 8.3.4 и ниже.
При необходимости работать с конфигурацией, которая содержит HTTP-сервисы, на системе «1С:Предприятие» версии 8.3.8 и выше, но которая открывалась конфигуратором системы «1С:Предприятие» версии 8.3.4, необходимо выполнить следующие действия:

Создать любой объект конфигурации;
Выполнить сохранение конфигурации информационной базы;
Удалить созданный объект конфигурации;
Выполнить сохранение конфигурации информационной базы."

понял) попробуем сделать действия которые там описанные. но главный акцент статьи все таки это Rest OData
4. VarLone 16.05.18 10:10 Сейчас в теме
После выхода продолжения, не забудь те в этой статье вставить ссылку на продолжение
vbuots; V.Stavinsky; +2 Ответить
6. dmarenin 22.05.18 17:25 Сейчас в теме
(0) код не читаем(не глагол)
V.Stavinsky; +1 Ответить
7. V.Stavinsky 183 22.05.18 18:58 Сейчас в теме
(8) спасибо, будет исправлено!
8. Dorosh 121 17.07.18 15:39 Сейчас в теме
Синхронный обмен через сервисы выглядит плохой идеей. Возможны большие задержки, юзеры будут смотреть на "зависший" 1с и нервничать. Переделайте отправку на асинхронный режим через фоновые задания.
9. V.Stavinsky 183 17.07.18 19:15 Сейчас в теме
(8) если менять пакеты данных - то да; но у нас один элемент справочника - одна "отправка" в другую базу; ответ приходит моментально; и уже в зависимости от ответа - если он отрицательный, то оставляем на "переотправку" фоновому заданию
10. Dorosh 121 19.07.18 09:06 Сейчас в теме
(9) Помимо размера отправляемых данных надо учитывать кол-во юзверей. Помимо задержек при транспорте есть ожидания на блокировках. Сейчас их у вас нет, завтра система вырастет вширь и они появятся.
12. V.Stavinsky 183 19.07.18 10:07 Сейчас в теме
(10) да, согласен, пока проблем нет; тогда в базе приемнике будем дописывать "очередь", чтобы избежать блокировок (и тут уже неважно как "направляются" данные туда - или онлайн, или фоново)
11. V.Stavinsky 183 19.07.18 10:07 Сейчас в теме
да, согласен, пока проблем нет; тогда в базе приемнике будем дописывать "очередь", чтобы избежать блокировок (и тут уже неважно как "направляются" данные туда - или онлайн, или фоново)
Оставьте свое сообщение