Эта работа является естественным продолжением моей предыдущей статьи "Практика доступа в базу 1С через протокол oData. Чтение данных" и все операции будут выполняться в той же демо-базе "Управление торговлей (базовая), редакция 10.3", к которой я предоставил доступ по OData в предыдущей статье.
Что нам нужно знать про HTTP?
OData - это протокол работы с данными поверх классического протокола HTTP. Это означает, что у каждого элемента данных есть свой URL, а чтобы сообщить серверу, что именно вам необходимо с этим элементом сделать (просто получить или изменить), используются HTTP-методы. Для многих эти сущности возможно будут новинкой. Ведь знание о HTTP-методах совершенно не нужно пользователям при их обычном интернет-серфинге (где тотально применяется GET). И даже веб-разработчики зачастую в курсе только про пару GET и POST, которые считают методами для отправки на сервер данных из форм.
Протокол OData подразумевает применение следующих пяти методов для работы с данными:
- GET - самый популярный из методов, который предназначен для запроса данных на чтение по указанному адресу;
- POST - метод для создания нового элемента данных, в качестве адреса обычно указывают только класс объектов;
- PUT - метод для замены свойств элемента данных на сервере по указанному полному адресу теми свойствами, которые передаются вместе с этим запросом. Обычно в популярных реализациях этого метода передача неполного состава свойств приводит к ошибке; так же часто адресация несуществующего элемента данных приводит к его автоматическому созданию (как это было реализовано в платформе 1С посмотрим ниже);
- PATCH - метод для обновления только переданных свойств элементов данных;
- DELETE - метод предназначенный для удаления элементов данных.
Клиент для протокола OData
Для "общения" с базой 1С по протоколу OData нам понадобится некоторый инструмент. И поскольку нас интересует уже не банальный GET, а полный спектр HTTP-методов, то обычные браузеры и подавляющее большинство популярных программ с поддержкой OData нам не подойдут. Потребуется что-то более функциональное.
Есть несколько распространенных вариантов. Опытные веб-разработчики, хорошо знакомые с сетевыми технологиями, на этих словах уже запускают свой любимый Fiddler, в котором на закладке Composer можно эмулировать любой из HTTP-методов, а потом, переключившись на закладку Inspectors, посмотреть полученный от сервера ответ. А поклонники UNIX-way, даже работая на Windows, конечно же имеют в наличии старый добрый автомат Калашникова консольный cUrl, в котором с помощью ключа -X можно указать требуемый HTTP-метод.
Но, поскольку мы тут все программисты 1С, то почему бы не написать собственный "велосипед"? Тем более что это очень просто и код можно в будущем использовать в других работах. Для этого поместим на форму новой обработки поля для подключения, авторизации, передачи параметров и вывода ответа сервера, а в обработчик на нажатие кнопки разместим примерно такой код:
Соединение = Новый HTTPСоединение(Сервер, Порт, Логин, Пароль,,,?(СоединениеЗащищено, Новый ЗащищенноеСоединениеOpenSSL(), Неопределено));
ЗапросСервера = Новый HTTPЗапрос(ПутьНаСервере);
ЗапросСервера.УстановитьТелоИзСтроки(ТелоЗапроса);
ОтветСервера = Соединение.ВызватьHTTPМетод(Метод, ЗапросСервера);
ТелоОтвета = ОтветСервера.ПолучитьТелоКакСтроку();
ЗаголовкиОтвета = "Status Code: " + ОтветСервера.КодСостояния + Символы.ПС;
Для каждого ЗначениеЗаголовка Из ОтветСервера.Заголовки Цикл
ЗаголовкиОтвета = ЗаголовкиОтвета + Символы.ПС +
ЗначениеЗаголовка.Ключ + " : " + ЗначениеЗаголовка.Значение;
КонецЦикла;
Если захотите ознакомится с моей обработкой во вложении, то там будет дополнительно разбор переданного URL с определением протокола (HTTP или HTTPS), вычисление порта и отбрасыванием из адреса прочую вспомогательную информацию. Выглядит примерно так:
Ломать - не строить
Для начала возьмем самую простую операцию - удаление, на примере контрагента "АОЗТ Лабан" из демо базы УТ10.3. Собственно повторяем вызов строчки из выше опубликованного скриншота, но вместо метода GET воспользуемся методом DELETE. Теперь вместо ответа 200 (все хорошо), сервер нам отвечает 204 (нет содержимого) - это нормальное поведение именно для удаления.
Давайте перейдем в саму базу 1С и по журналу регистрации проверим, что же произошло:
Как мы видим, наш контрагент был успешно удален вместе со всеми своими подчиненными связанными данными. Тот факт, что для всех этих справочников в роли моего пользователя было запрещено интерактивное удаление, не имеет значения. Удаление по протоколу OData приравнивается к удалению из программного кода. Но, как видим, есть и различие - при программном удалении контрагента его подчиненные элементы остались бы в базе. Это важно и потому запоминаем: если у хоть одной роли пользователя есть право "Удаление", то у вас есть риск на безвозвратную потерю множества данных без контроля на наличие ссылок:
Кстати, повторная попытка удалить элемент с помощью метода DELETE аналогично запросу с помощью GET приведет к ошибке 404 (не найдено):
Создаем новое (или старое)
Как жаль, что контрагента "АОЗТ Лабан" постигла такая печальная участь... А можем ли мы его спасти без восстановления базы из бэкапа или без выполнения кода непосредственно в УТ?
Начну с цитаты из документации:
Для создания объекта следует воспользоваться POST-запросом с использованием URL набора сущностей, передав в теле запроса документ (в поддерживаемом формате), который содержит значения полей создаваемого объекта. Если передаваемый документ содержит свойства, отсутствующие у создаваемого объекта, то эти свойства игнорируются.
Больше ничего у нас с вами нет. Ни перечня обязательных полей. Ни перечня исключаемых полей. Ни упоминания о возможности создания объекта с нужным нам значением GUID. Поэтому начинаем свободные эксперименты!
Еще с самого первого запроса GET у меня осталось описание контрагента, со свойством Ref_Key, в котором находился его GUID (даже если бы его у меня не было, то я мог получить его из документа, который все еще ссылается на удаленного контрагента). Сначала попробуем отдать полностью это описание на URL с указанием элемента. Ожидаемо получаем ошибку 404 как и в случае методов GET и DELETE (см. выше). Теперь обращаемся к "набору сущностей", как того требует документация:
http://localhost/DemoTrdBase/odata/standard.odata/Catalog_Контрагенты?$format=json
И на этот раз у нас все получилось:
Более того, все получилось именно так как и было задумано - новый элемент был создан именно с тем GUID (а следовательно с той же ссылкой) как и ранее удаленный! Это хорошо видно на документе закупки ТК000000030 от 13.03.2007, где контрагент появился, но его договора и типа цен по прежнему нет (их можно аналогично восстановить, воспользовавшись в качестве образца для структуры подчиненные данные похожего контрагента, лишь в качестве GUID подставив правильные строки):
Это все превосходно, но а если у объекта множество свойств и они нам сейчас не интересны (пусть будут значения по умолчанию). Можем ли мы уменьшить размер передаваемого пакета и следовательно увеличить скорость гипотетического создания множества объектов в цикле? Для ответа на этот вопрос пойдем по пути экстремальной минимизации - попробуем создать элемент не указав ему вообще ни единого свойства!
Отлично, выходит, что нам нет нужды перечислять все значения по-умолчанию и платформа за нас все сделает сама. Но справочник - это слишком просто. Интереснее попробовать с документом, при записи которого, как мы знаем, обычно возникает ошибка платформы, если не указать дату. Попробуем на примере упомянутой выше приходной накладной:
Не то чего я ожидал, но тем не менее отличный результат! Следовательно все программно-обрабатываемые события объектов не игнорируются! Это вам не тупой прямой insert в СУБД, а процедура записи полностью идентичная программной записи из какой-нибудь обработки. Но все же, что же будет с датой? Для этого нам нужен более простой документ, который крепко не привязан к рабочим процессам в УТ, к примеру Событие:
Да, ошибка получена. Теперь лично у меня к механизму создания элементов методом POST вопросов больше не осталось. Все оказалось довольно простым и без "заморочек": можно устанавливать уникальный идентификатор новым ссылкам, отсутствуют какие-либо требования к обязательным или запрещенным свойствам в описании, лишние (не относящиеся к объекту) свойства просто игнорируются, запись в базу следует всем тем же правилам, что и обычная программная запись.
Теперь будем изменять (в хорошем смысле)
Как я уже упомянул выше, для изменения данных в базе 1С посредством OData можно использовать методы PUT и PATCH.
Давайте сразу же попробуем применить PUT в созидательной роли POST. К сожалению, тут ничего интересного мы не узнаем. В контексте коллекции мы получаем ошибку, что данный метод недопустим, а при указании произвольного GUID мы получим ошибку, что изменяемый экземпляр не найден:
А что если не указывать все реквизиты? Действительно, практика показала, что можно не указывать все реквизиты объекта и эти свойства станут заполняться значениями по умолчанию, что иногда может быть удобным. Ниже пример, где у справочника были затерты введенные вручную полное наименование и параметры прописи для валюты:
Вот и все интересное, что мы могли узнать про PUT, и потому переходим к более гибкому методу PATCH. При работе с этим методом, в отличии от его предшественника, нам для установки значения единичного реквизита больше не нужно делать предварительный GET для запроса значений всех свойств, что бы потом эти же свойства не затереть значениями по-умолчанию. Нам достаточно лишь идентификатора объекта и нового значения указанного свойства:
Теоретически, данный метод мог бы быть очень удобным для быстрого массового изменения элементов с помощью единого вызова. На пример, чтобы сменить основного менеджера для всех поставщиков, пометить на удаление документы без суммы и так далее. К сожалению, компания 1С не дала нам такую интересную возможность; для массового изменения нам доступна лишь только серия вызовов в цикле:
Итог
Не смотря на отсутствие некоторых удобств, протокол OData на платформе "1С:Предприятие 8" позволил нам совершать все базовые операции над данными. Таким образом, имея доступ в базу только по этому протоколу, можно реализовать полноценную интеграцию с некоторой внешней системой.
Статья вышла длинной не смотря на то, что были рассмотрены самые простые операции. Часть возможных интересных экспериментов была оставлена за кадром, но вы можете самостоятельно попрактиковаться. К примеру, как на счет записать набор записей по какому-нибудь из регистров? ;)