Друзья, наверное, каждый сталкивался с задачей формирования ответа на http запрос, и в целом решение в лоб можно свести к следующим шагам:
1. Чтение структуры входящих параметров
2. Запрос данных / вычисления
3. Формирование структуры ответа
// 1.
ВходящиеПараметрыСтрока = HTTPЗапрос.ПолучитьТелоКакСтроку();
ЧтениеJSON = Новый ЧтениеJSON;
ЧтениеJSON.УстановитьСтроку(ВходящиеПараметрыСтрока);
ВходящиеПараметры = ПрочитатьJSON(ЧтениеJSON);
// 2.
ЧтоТоДелаем(ВходящиеПараметры);
// 3.
Ответ = Новый Структура;
Ответ.Вставить("Атрибут1", 1)
ЗаписьJSON = Новый ЗаписьJSON;
ЗаписатьJSON(ЗаписьJSON, Ответ);
Казалось бы, все хорошо, давай напишем обработку нескольких десятков запросов. Написали, применили все свои навыки, получилось просто отлично, в хранилище тысячи строк нового кода. Сонар счастлив, все счастливы, все хорошо поработали.
Супер, развиваем успех: добавляем обработку новых запросов, новую функциональность, в хранилище новые тысячи строк кода.
И все это великолепие рушится одним вопросом: "Ребят, а какое у нас API? Заказчик хочет отдать своим клиентам описание в формате OpenAPI для интеграции".
А что сделаешь?
- Мы не знаем, что каждый метод ожидает в качестве входящих параметров: типы, доступные значения, атрибуты, обязательные атрибуты, примечания.
- Мы не знаем, что каждый метод отдает: типы, возможные значения, атрибуты, примечания.
- Мы ничего не знаем. Что-то приходит, что-то отдаем.
Проходит минута, и ты уже понимаешь, что эти тысячи строк кода, возможно, годы работы превращаются в мусор.
Как спасти, что предпринять?
1. Можно пробежаться по каждому методу, описать на "бумаге", что ожидается на входе, что в ответе. Но в таком случае ни о какой актуальности речи быть не может. Раз сделали, отдали и забыли.
2. Можно к текущей обработке запроса подстроить какой нибудь механизм формирования описаний. Возможно, это решение, а возможно, просто обертка для обхода все тех же ошибок.
А что случилось? Было же хорошо, кто все испортил?
На вход пришел JSON, где-то ссылки, где-то числа, строки, прочитали в структуру. Что-то сделали, сформировали структуру в ответ, сериализовали в JSON. Все строится вокруг объекта "Структура": в него читаем, из него выгружаем. У структуры есть ключ и значение. "Ключ" - имя атрибута, "Значение" - содержимое атрибута. А где разместить признаки обязательности, типов и доступных значений для атрибута? Верно, в еще одной структуре в значении. Слишком много структур.
А что, если объект "Структура" для формирования ответа внешним системам неприменим и мы совершили ошибку в самом начале, а все, что дальше, это просто костыли? Давай заменим её на "Контейнер объекта")))
"Контейнер объекта" представляет собой набор из четырех обработок (объектов), каждая из которых одновременно является независимой и зависит от других. Вместе они формируют комплексный функционал для описания структуры/коллекций, открывая безграничные возможности расширения её свойств.
1. Обработка.сп_КонтейнерОбъекта - Объект, описывающий хранимые объекты (структуры).
2. Обработка.сп_КонтейнерКоллекции - Объект, описывающий хранимые коллекции (массивы).
3. Обработка.сп_КонтейнерЗначения - Объект, описывающий хранимое значение.
4. Обработка.сп_КонтейнерСвойства - Объект, описывающий свойства хранимого объекта (ключа структуры).
//{
// "Атрибут1": null,
// "Атрибут3": {
// "Поле1": "Привет из контекста 2!"
// },
// "Атрибут4": [],
// "Атрибут5": "Привет!",
// "Атрибут6": "Привет из контекста 1!"
//}
// Инициализация ответа
Ответ = Обработки.сп_КонтейнерОбъекта.Инициализировать();
// Атрибут1 (Строка)
// Добавим новое свойство в объект ответа с типом строка
Атрибут1 = Обработки.сп_КонтейнерЗначения.Инициализировать(Новый ОписаниеТипов("Строка"));
Атрибут1Свойство = Ответ.СвойствоДобавить("Атрибут1", Атрибут1);
// Атрибут2 (Ссылка)
// При добавлении свойства в объект установим отказ от вывода в результат в случае если не заполнено / не установлено
Атрибут2 = Обработки.сп_КонтейнерЗначения.Инициализировать(Новый ОписаниеТипов("СправочникСсылка.Валюты"));
Атрибут2Свойство = Ответ.СвойствоДобавить("Атрибут2", Атрибут2);
Атрибут2Свойство.ВыводитьПустое = Ложь;
// Атрибут3 (Структура)
// Добавим в ответ описание вложенной структуры
Атрибут3 = Обработки.сп_КонтейнерОбъекта.Инициализировать();
Атрибут3Свойство = Ответ.СвойствоДобавить("Атрибут3", Атрибут3);
// Атрибут3.Поле1 (Число)
Поле1 = Обработки.сп_КонтейнерЗначения.Инициализировать(Новый ОписаниеТипов("Число"));
Поле1Свойство = Атрибут3.СвойствоДобавить("Поле1", Поле1);
// Атрибут4 (Массив структур)
// Добавим массив структур
Атрибут4Строка = Обработки.сп_КонтейнерОбъекта.Инициализировать();
// Атрибут4.Поле2
Поле2 = Обработки.сп_КонтейнерЗначения.Инициализировать(Новый ОписаниеТипов("Булево"));
Поле2Свойство = Атрибут4Строка.СвойствоДобавить("Поле2", Поле2);
Атрибут4 = Обработки.сп_КонтейнерКоллекции.Инициализировать(Атрибут4Строка);
Атрибут4Свойство = Ответ.СвойствоДобавить("Атрибут4", Атрибут4);
// Атрибут5 (Оперативная установка значения)
// Выполним установку значения свойства "онлайн"
Атрибут5 = Обработки.сп_КонтейнерЗначения.Инициализировать(Новый ОписаниеТипов("Строка"));
Атрибут5Свойство = Ответ.СвойствоДобавить("Атрибут5", Атрибут5);
Атрибут5.ЗначениеУстановить("Привет!");
// Атрибут6 (Отложенная установка значения из контекста)
// Для значения свойства укажем адрес по которому будет получено значение при выгрузке
Атрибут6 = Обработки.сп_КонтейнерЗначения.Инициализировать(Новый ОписаниеТипов("Строка"));
Атрибут6Свойство = Ответ.СвойствоДобавить("Атрибут6", Атрибут6);
Атрибут6.ЗначениеУстановитьИзКонтекста("Контекст.Атрибут6");
// Поиск и установка значения в ответ
Ответ.ЗначениеУстановить("Атрибут3.Поле1", "Оперативный привет!");
Ответ.ЗначениеУстановитьИзКонтекста("Атрибут3.Поле1", "Контекст.Атрибут3Поле1");
// Выгрузка ответа
Результат = Ответ.Выгрузить();
// Выгрузка описаний ответа
// В значение для каждого свойства выполняется вывод описаний типов заданных при инициализации свойства
// Данный режим выгрузки возможно использовать для реализации получения типов/доступных значений свойств и т.д.
Результат = Ответ.Выгрузить("Описания");
// Выгрузка с переданным контекстом
// Нет необходимости при формировании ответа устанавливать значения свойств "онлайн".
// Как вариант использования, возможно запустить фоновые задания для получения данных и продолжить формирование ответа.
// А когда данные будут готовы, выгрузить ответ в контексте сформированном в фоне.
Контекст = Новый Структура;
Контекст.Вставить("Атрибут6" , "Привет из контекста 1!");
Контекст.Вставить("Атрибут3Поле1" , "Привет из контекста 2!");
ВыгрузитьПараметры = Ответ.ВыгрузитьПараметрыИнициализировать();
ВыгрузитьПараметры.Режим = "ЗначенияИзКонтекста";
ВыгрузитьПараметры.Контекст = Контекст;
Результат = Ответ.Выгрузить(ВыгрузитьПараметры);
// Частичная выгрузка ответа, отдельного свойства или коллекции
Результат = Ответ.ЗначениеНайтиИВыгрузить("Атрибут3", ВыгрузитьПараметры);
В чем преимущество?
1. Описание объекта (входящей структуры / структура ответа) изолировано от данных, и они формируются независимо друг от друга. Хочешь, запускай в параллели.
2. Возможность реализации "статической типизации". Все значения свойств устанавливаются через единый метод, легко реализовать проверку типов.
3. Свойства атрибута это отдельный объект, со своими настройками. Легко добавить свои, предположим: ДоступныеЗначения (для проверки не только устанавливаемого типа, но и самого значения)
Как видите, все просто, данная разработка в помощь!
Обработка является частью модуля Стандартные подсистемы.
Проект и исходный код доступен на GitLab.
-----
Требования:
- Обработка предназначена для версии платформы >= 8.3.13, тестировалась на 8.3.21.1895
-----
Ссылки:
- Проект в GitLab
Проверено на следующих конфигурациях и релизах:
- 1С:Библиотека стандартных подсистем, редакция 3.1, релизы 3.1.1.91
Вступайте в нашу телеграмм-группу Инфостарт