Для записи данных в REST используются: метод POST протокола HTTP для создания новых элементов и метод PATCH HTTP для обновления существующих данных. Например, запишем в базу контрагента. Сначала устанавливаем HTTPСоединение с базой. Создаем объект HTTPЗапрос. Подготовим параметр АдресРесурса запроса:
СтрокаЗапроса = "/Base1C/odata/standard.odata/Catalog_Контрагенты";
Для PATCH вызова допишем:
Если НЕ ПустаяСтрока(ID) Тогда
СтрокаЗапроса = СтрокаЗапроса + "(guid'" + ID + "')";
КонецЕсли;
Подготовим заголовки запроса и создадим его.
Заголовки = Новый Соответствие;
Заголовки.Вставить("Accept", "application/atom+xml,application/xml");
Заголовки.Вставить("Accept-Charset", "UTF-8");
Запрос = Новый HTTPЗапрос(СтрокаЗапроса, Заголовки);
Потом формируем текст запроса, для обоих методов он одинаков.
ТекстЗапроса = "<?xml version=""1.0"" encoding=""UTF-8""?>
|<entry xmlns=""http://www.w3.org/2005/Atom""
| xmlns:d=""http://schemas.microsoft.com/ado/2007/08/dataservices""
| xmlns:m=""http://schemas.microsoft.com/ado/2007/08/dataservices/metadata""
| xmlns:georss=""http://www.georss.org/georss""
| xmlns:gml=""http://www.opengis.net/gml"">
| <content type=""application/xml"">
| <m:properties>"
+ ?(ПустаяСтрока(ID), "", "
| <d:Ref_Key>" + ID + "</d:Ref_Key>")
+ ?(ПустаяСтрока(Code), "", "
| <d:Code>" + Code + " </d:Code>") + "
| <d:DeletionMark>false</d:DeletionMark>"
+ ?(ПустаяСтрока(Name), "", "
| <d:Description>" + Name + "</d:Description>"
+ ?(ПустаяСтрока(INN), "", "
| <d:ИНН>" + INN + "</d:ИНН>")
);
Если IsGroup Тогда
ТекстЗапроса = ТекстЗапроса + "
| <d:IsFolder>true</d:IsFolder>
| </m:properties>
| </content>
|</entry>
|";
Иначе
Если ПустаяСтрока(INN) ИЛИ СтрДлина(INN)=12 Тогда
ЮридическоеФизическоеЛицо="ФизическоеЛицо";
Иначе
ЮридическоеФизическоеЛицо="ЮридическоеЛицо";
КонецЕсли;
ТекстЗапроса = ТекстЗапроса + "
| <d:IsFolder>false</d:IsFolder>"
+ ?(ПустаяСтрока(FullName), "", "
| <d:НаименованиеПолное>" + FullName + "</d:НаименованиеПолное>")
+ ?(ПустаяСтрока(INN), "", "
| <d:ИНН>" + INN + "</d:ИНН>")
+ ?(ПустаяСтрока(KPP), "", "
| <d:КПП>" + KPP + "</d:КПП>") + "
| <d:ЮридическоеФизическоеЛицо>" + ЮридическоеФизическоеЛицо + "</d:ЮридическоеФизическоеЛицо>" + "
| </m:properties>
| </content>
|</entry>
|";
КонецЕсли;
Текст запроса начинается с обязательной шапки. Дальше, в тэге <m:properties> перечисляются записываемые реквизиты. Каждый реквизит записывается тэгом «</d:название реквизита>. Реквизиты ссылочного типа после имени получают суффикс _Key. Значение типа перечисление записывается строковым представлением.
Теперь выполним HTTP запрос:
Запрос.УстановитьТелоИзСтроки(ТекстЗапроса);
Если ПустаяСтрока(ID) Тогда
Ответ = HTTPсоединение.ВызватьHTTPМетод("POST", Запрос); // Создаем новый элемент
Иначе
Ответ = HTTPсоединение.ВызватьHTTPМетод("PATCH", Запрос); // Update'им существующий
КонецЕсли;
Проанализируем ответ:
ОтветСтрокой = Ответ.ПолучитьТелоКакСтроку();
Если Ответ.КодСостояния > 299 Тогда
ТекстОшибки = "Error, код ошибки: " + Ответ.КодСостояния + "
|" + ОтветСтрокой;
ИначеЕсли ПустаяСтрока(ID) Тогда
// GUID не был передан заранее, значит нужно найти в ответе и передать назад
КолСтрок = СтрЧислоСтрок(ОтветСтрокой);
Для НомерСтроки=1 По КолСтрок Цикл
СтрокаАнализа = СтрПолучитьСтроку(ОтветСтрокой, НомерСтроки);
ПозицияНачала = СтрНайти(СтрокаАнализа, "<d:Ref_Key>");
Если ПозицияНачала > 0 Тогда
ПозицияНачала = ПозицияНачала + 11;
ID = Сред(СтрокаАнализа, ПозицияНачала, 36);
Прервать;
КонецЕсли;
КонецЦикла;
БулевРезФун = Истина;
ТекстОшибки = "OK. Был создан новый элемент с GUID='" + ID + "'";
Иначе
БулевРезФун = Истина;
ТекстОшибки = "OK. Успешно обновлен элемент с GUID='" + ID + "'";
КонецЕсли;
Теперь документ. Запишем платежку.
ТекстЗапроса = "<?xml version=""1.0"" encoding=""UTF-8""?>
|<entry xmlns=""http://www.w3.org/2005/Atom""
| xmlns:d=""http://schemas.microsoft.com/ado/2007/08/dataservices""
| xmlns:m=""http://schemas.microsoft.com/ado/2007/08/dataservices/metadata""
| xmlns:georss=""http://www.georss.org/georss""
| xmlns:gml=""http://www.opengis.net/gml"">
| <content type=""application/xml"">
| <m:properties>
| <d:Ref_Key>" +GUID + "</d:Ref_Key>")
| <d:Number>" +Number + " </d:Number>") + "
| <d:DeletionMark>false</d:DeletionMark>
| <d:РаспределятьОплатуАвтоматически>true</d:РаспределятьОплатуАвтоматически>
| <d:Организация_Key>" + OrgID + "</d:Организация_Key>
| <d:ВалютаДокумента_Key>" + ВалютаДокумента + "</d:ВалютаДокумента_Key>
| <d:Date>" + XMLСтрока(Date) + "</d:Date>
| <d:СуммаДокумента>" +Summa + "</d:СуммаДокумента>
| <d:Комментарий> Создан из мобильного клиента " + ТекущаяДата() + "</d:Комментарий>";
С реквизитами составного типа немного сложнее:
Если ЗначениеЗаполнено(Контрагент) Тогда
ТекстЗапроса = ТекстЗапроса + "
| <d:Контрагент_Type>StandardODATA.Catalog_Контрагенты</d:Контрагент_Type>
| <d:Контрагент>" +Контрагент + " </d:Контрагент>";
ИначеЕсли ЗначениеЗаполнено(Физлицо) Тогда
ТекстЗапроса = ТекстЗапроса + "
| <d:Контрагент_Type>StandardODATA.Catalog_ФизическиеЛица</d:Контрагент_Type>
| <d:Контрагент>" + Физлицо + " </d:Контрагент>";
Иначе
ТекстОшибки = "Error - должен быть заполнен контрагент";
Возврат БулевРезФун;
КонецЕсли;
Для реквизитов составного типа сначала задается тип записываемого значения, тэгом <d:название реквизита_Type>. Потом ему присваивается значение. Любопытно, но значение реквизита в этом случае задается без суффикса _Key.
Теперь нужно описать табличную часть документа.
ТекстЗапроса = ТекстЗапроса + "
| <d:РасшифровкаПлатежа m:type=""Collection(StandardODATA.Document_ПоступлениеНаРасчетныйСчет_РасшифровкаПлатежа_RowType)"">
Тэг <d:РасшифровкаПлатежа указывает имя табличной части. Тэг m:type указывает тип реквизита РасшифровкаПлатежа. Теперь в цикле опишем значения строк табличной части:
Для НомерСтроки=1 По Док. РасшифровкаПлатежа.Количество()-1 Цикл
ТекстЗапроса = ТекстЗапроса + "
| <d:element m:type=""StandardODATA.Document_ПоступлениеНаРасчетныйСчет_Товары_RowType"">
| <d:LineNumber>"+НомерСтроки+</d:LineNumber>
| <d:КурсВзаиморасчетов>1</d:КурсВзаиморасчетов>
| <d:КратностьВзаиморасчетов>1</d:КратностьВзаиморасчетов>
| <d:СуммаПлатежа>" + Summa[НомерСтроки-1] + "</d:СуммаПлатежа>
| <d:СуммаВзаиморасчетов>" + Summa[НомерСтроки-1] + "</d:СуммаВзаиморасчетов>
| <d:СтавкаНДС>БезНДС</d:СтавкаНДС>
| </d:element>";
КонецЦикла;
Тэг d:element используется для описание строк табличной части. Внутри описания элемента тэг m:type указывает тип строки табличной части. Тэг d:LineNumber необходим для указания номера строки.
Закончим текст запроса.
ТекстЗапроса = ТекстЗапроса + "
| </d:РасшифровкаПлатежа>
| </m:properties>
| </content>
|</entry>
|";
Подготовим АдресРесурса:
СтрокаЗапроса = "/"/Base1C/odata/standard.odata/Document_ПоступлениеНаРасчетныйСчет";
и заголовки HTTP запроса:
Заголовки = Новый Соответствие;
Заголовки.Вставить("Accept", "application/atom+xml,application/xml");
Заголовки.Вставить("Accept-Charset", "UTF-8");
Заголовки.Вставить("1C_OData_DataLoadMode", Истина);
Для документов доступна запись в режиме ОбменДанными.Загрузка=Истина;
Наконец-то можно выполнить запрос:
Запрос = Новый HTTPЗапрос(СтрокаЗапроса, Заголовки);
Запрос.УстановитьТелоИзСтроки(ТекстЗапроса);
Ответ = HTTPсоединение.ОтправитьДляОбработки(Запрос);
Обработаем ответ:
ОтветСтрокой = Ответ.ПолучитьТелоКакСтроку();
КолСтрок = СтрЧислоСтрок(ОтветСтрокой);
Если Ответ.КодСостояния > 299 Тогда
ТекстОшибки = "Error, код ошибки: " + Ответ.КодСостояния + "
|" + ОтветСтрокой;
Возврат БулевРезФун;
Иначе
Если ПустаяСтрока(Number) Тогда
Number = " Не удалось определить № документа ";
Для НомерСтроки=1 По КолСтрок Цикл
СтрокаАнализа = СтрПолучитьСтроку(ОтветСтрокой, НомерСтроки);
ПозицияКонца = СтрНайти(СтрокаАнализа,"</d:Number>");
Если ПозицияКонца > 0 Тогда
ПозицияНачала = СтрНайти(СтрокаАнализа,">") + 1;
Number = Сред(СтрокаАнализа, ПозицияНачала, ПозицияКонца - ПозицияНачала);
КонецЕсли;
КонецЦикла;
КонецЕсли;
БулевРезФун = Истина;
ТекстОшибки = "OK. Успешно создано поступление на расчетный счет с номером='" + Number + "'";
КонецЕсли;
Заключение.
Итак, для чего нужны такие хлопоты? Главный и огромный плюс REST — быстродействие. Тесты на живых данных показали, по сравнению с привычными SOUP сервисами REST отрабатывали в 3-10 раз быстрее. Ради такого прироста можно помучиться и перетерпеть все неудобства REST.
хмм, а как вы тестировали? В данном случае вы в REST уже отправляете готовый элемент справочника в виде xml, никакой сложной обработки.
если в веб-сервисе не писать никакой логики, а только с помощью десериализации из xml (по-моему с помощью СериализаторXDTO.ПрочитатьXML) создать элемент справочника, то откуда берется такая большая разница по скорости в 3-10 раз?
Формирование XML через конкатенацию — отвратительно. Можно очень просто сломать.
(2) Согласен, но деваться некуда, другого способа нет. Еще один минус в сторону REST.
(3) начиная от ЗаписьXML и заканчивая СериализаторXDTO
(4) И как с их помощью можно составить текст запроса для REST?
(5) Полагаю, как то так (накидал пример на коленке):
Показать
Получим что типа такого:
Показать
(3) Вообще то ODATA создаваласть для интеграции с любыми системами. А там как раз используют объекты
Linq to ODATA
Не SOUP, а SOAP поправь опечатку.
А почему не использовать JSON? По мне так он намного проще. По времени записи наверняка xml не уступает.