Начало: Слои и модуль Сервиса, Валидация данных, Провайдер данных
В прошлых статьях, мы выяснили, что можем подходить к разработке модулей сервиса независимо. У нас отсутствуют циклические зависимости модулей, нет взаимного вызова друг друга.
Контроллер занимает центральное место в сервисе, отвечает на команды модуля сервиса, может изменить предложенный код состояния и добавить заголовки ответа.

Еще раз о всех слоях и их взаимодействие с Контроллером.
Модуль Сервиса вызывает Контроллер и ничего не знает о модулях: Валидации данных, Провайдере данных.
-
Передает в параметрах методов Контролера: данные http-запроса и ссылку на КодОшибки.
-
Некоторые http-методы, могут потребовать дополнительные заголовки или специфический код состояний, тогда в параметры методов Контроллера добавляются ссылки на заголовки и код состояния.
-
Ожидает получить конкретные данные или не пустой КодОшибки.
-
Не содержит вызовов встроенных подсистем.
-
При необходимости может собирать тексты ошибок из пула ошибок и делать запись в журнал регистрации.
-
Формирует код состояния ответа (он либо заранее известен, либо зафиксирован в описании ошибки, либо возвращается из метода Контроллера по ссылке).
-
Формирует заголовки: типа содержимого, добавляет в заголовок Location, текст ошибки при необходимости переадресации.
-
В свою очередь Модуль Контроллера не знает о Модуле Сервиса.
Модуль Валидации данных, ничего не знает о сервисе, о БД.
-
Выполняет команды Контроллера по проверке входящих данных.
-
Проверка может быть точечная или массовая.
-
Для массовой проверки содержит в интерфейсе структуру правил и условий, которую Контроллер использует для подготовки наборов проверок.
-
Формирует сообщения пользователя о возникших ошибках при проверках. Тексты ошибок получает от Контроллера напрямую или в наборах правил/условий.
Модуль Провайдер данных содержит логику доступа к данным БД, для создания новых объектов БД и ничего не знает о сервисе.
-
Выполняет команды Контроллера и возвращает сырые данные из БД.
-
Предоставляет конструкторы для подготовки параметров доступа к данным и параметров для создания новых объектов, которые Контроллер должен заполнить
Модуль Контроллера содержит бизнес-логику сервиса, знает о существовании модулей Валидации и Провайдере данных, о входящих данных и формате выходящих. Его задачи:
-
Выполнить команды от модуля Сервиса, на выходе конкретные данные или Неопределено с заполненным КодОшибки
-
Формирует соответствие(связь) между входными и выходными параметрами.
-
Проверить специфические входящие данные: ожидаемые заголовки (и их значения), формат тела запроса и состав содержимого.
-
Дать команду модулю ВалидацияДанных проверить входящие данные, передав текст ошибки, который необходимо поместить в пул ошибок при негативной проверке
-
По завершению проверки входящих данных принимает решение о продолжении или заполняет КодОшибки
-
Дать команду модулю Провайдер данных получить данные из БД или создать новые объекты, передав преобразованные входящие данные
-
Преобразовать ответ от Провайдера данных в форму, которую ожидает модуль Сервиса или заполнить КодОшибки
-
При необходимости изменить полученные по ссылке Код и добавить Заголовки ответа.
Соответствие (связь) между входными и выходными параметрами
В статье о модуле Валидации, раздел Массовая проверка, мы рассматривали пример получения списка задач. Задачи получаем порциями, на задачи может быть наложен фильтр. Какой способ пагинации выбран и какой установлен фильтр мы узнаем из параметров запроса.
Функция СписокЗадачСоответствиеКлючей()
Результат = Новый Соответствие;
Результат.Вставить("ДатаНачала", "startDate");
Результат.Вставить("ДатаОкончания", "endDate");
Результат.Вставить("ИдентификаторИсполнителя", "userId");
Результат.Вставить("ИдентификаторАвтора", "authorId");
Результат.Вставить("НомерСтраницы", "page");
Результат.Вставить("ДоКурсора", "before");
Результат.Вставить("ПослеКурсора", "after");
Результат.Вставить("Лимит", "limit");
Возврат Результат;
КонецФункции
Ключи в этой структура – параметры доступа к данным, а значения – параметры входных данных, которые мы получаем из http-запроса (Запрос.ПараметрыЗапроса). Эта структура, вместе с наборами правил и условий будет передана в метод, который проверит по ним (наборам) входящие данные:
ПараметрыЗапроса = ВалидацияДанных.РезультатВалидации(
Запрос.ПараметрыЗапроса,
СписокЗадачСоответствиеКлючей(),
НаборПравилВалидацииПараметровСпискаЗадач(),
НаборУсловийПараметровСпискаЗадач(),
Отказ
);
Отказ в Истина на выходе, будет означать, что проверка провалилась. Результатом будет структура, ключи – параметры доступа, значения – полученные из запроса и проверенные данные.
Окончательный вид параметры доступа приобретут, когда Контроллер запросит структуры для фильтра, для настройки получения и сопоставит с тем, что пришло и прошло проверку:
ФильтрЗадач = РаботаСЗадачамиРеализация.НовыйФильтрЗадач();
ЗаполнитьЗначенияСвойств(ФильтрЗадач, ПараметрыЗапроса);
…
НастройкиПолученияЗадач = РаботаСЗадачамиРеализация.НовыеНастройкиПолученияЗадач();
ЗаполнитьЗначенияСвойств(НастройкиПолученияЗадач, ПараметрыЗапроса);
Список задач получаем в сыром виде, как выгрузку:
ДанныеЗадач = РаботаСЗадачамиРеализация.СписокЗадачВыгрузка(НастройкиПолученияЗадач, ФильтрЗадач, ЗадачНаСтраницу);
...
// Контроллер ничего не знает о встроенных подсистемах, поэтому за этим тоже обращаемся к Провайдеру данных
ДанныеЗадач = РаботаСЗадачамиРеализация.ТаблицаЗначенийВМассив(ДанныеЗадач);
...
Возврат ПодготовленныеЗадачи(ДанныеЗадач, ФайлыЗадач);
Сырые данные из БД и данные, которые ждет Сервис разные. Подготовка также через соответствие:
Функция СвойстваЗадачиСоответствие()
Результат = Новый Соответствие;
Результат.Вставить("УникальныйИдентификатор", "ID");
Результат.Вставить("Номер", "TaskID");
Результат.Вставить("Приоритет", "Priority");
Результат.Вставить("Содержание", "Text");
Результат.Вставить("Исполнитель", "UserID");
Результат.Вставить("Автор", "AuthorID");
Возврат Результат;
КонецФункции
Функция ПодготовленныеЗадачи(ДанныеЗадач, ФайлыЗадач = Неопределено)
Результат = Новый Массив;
Соответствие = СвойстваЗадачиСоответствие(); // через соответствие!
Для Каждого СвойстваОбъекта Из ДанныеЗадач Цикл
Приемник = Новый Структура;
Для каждого Источник Из Соответствие Цикл
Приемник.Вставить(Источник.Значение, СвойстваОбъекта[Источник.Ключ]);
КонецЦикла;
Ключ = Соответствие.Получить("Приоритет"); // особое свойство
Значение = Неопределено;
Если Приемник.Свойство(Ключ, Значение) Тогда
Приемник.Вставить(Ключ, ПриоритетЗадачиИзОбъекта(Строка(Значение)))
КонецЕсли;
Если ФайлыЗадач <> Неопределено Тогда
Файлы = ФайлыЗадач[СвойстваОбъекта.Ссылка];
Приемник.Вставить("Файлы", ?(Файлы = Неопределено, Новый Массив, Файлы));
КонецЕсли;
Результат.Добавить(Приемник);
КонецЦикла;
Возврат Результат
КонецФункции
Создание новой задачи
Еще один пример – создание задачи, данные задачи могут прийти как JSON, или как поля формы. Результат может быть перенаправлен.
-
проверить тип содержимого и целостность данных – контроль заголовков и их значений
-
по конструктору НовыеСвойстваЗадачи() сопоставить с входящими данными:
ЗаполнитьЗначенияСвойств(СвойстваЗадачи, ВходящиеСвойстваЗадачи);
-
получить параметры заполнения:
-
заполняя параметры заполнения проверять их корректность, пример:
-
проверку обязательности ключевых реквизитов доверить БД, результат отследить через Отказ и через заполнение СвойстваЗадачи.TaskID:
-
при необходимости указать на перенаправление: дополнить заголовком и изменить код
Вызов из модуля Сервиса, рассмотрен полностью в статье о модуле Сервиса:
КодОшибки = ""; Код = 201;
НоваяЗадача = РаботаСЗадачамиHTTP.НоваяЗадачаСотрудника(Запрос, КодОшибки, Заголовки, Код);
Функция НоваяЗадачаСотрудника(Запрос, КодОшибки, ЗаголовкиРезультат, КодРезультат) Экспорт
УстановитьПривилегированныйРежим(Истина);
ТипКонтента = ЗначениеПоКлючуВЗаголовках(Запрос.Заголовки, "Content-Type");
ТелоЗапроса = Запрос.ПолучитьТелоКакСтроку();
ВходящиеСвойстваЗадачи = Неопределено;
Попытка
Если СтрНачинаетсяС(НРег(ТипКонтента), "application/json") Тогда
ВходящиеСвойстваЗадачи = ПрочитатьЗначениеJSON(ТелоЗапроса);
ИначеЕсли СтрНачинаетсяС(НРег(ТипКонтента), "application/x-www-form-urlencoded") Тогда
ВходящиеСвойстваЗадачи = ЗадачаПоляUrlEncoded(ТелоЗапроса);
Иначе
КодОшибки = "ТипТела";
Возврат Неопределено;
КонецЕсли;
Исключение
КодОшибки = "ФорматТела";
Возврат Неопределено;
КонецПопытки;
Если ТипЗнч(ВходящиеСвойстваЗадачи) <> Тип("Структура") Тогда
КодОшибки = "ФорматТела";
Возврат Неопределено;
КонецЕсли;
СвойстваЗадачи = НовыеСвойстваЗадачи();
ЗаполнитьЗначенияСвойств(СвойстваЗадачи, ВходящиеСвойстваЗадачи);
Отказ = Ложь;
ПараметрыЗаполнения = РаботаСЗадачамиРеализация.ЗадачаСотрудникаПараметрыЗаполнения();
ПараметрыЗаполнения.Приоритет = ПриоритетЗадачиПоИндентификатору(СвойстваЗадачи.Priority, Отказ);
ПараметрыЗаполнения.Содержание = СвойстваЗадачи.Text;
ПараметрыЗаполнения.Исполнитель = ПользовательПоИдентификатору(СвойстваЗадачи.UserID, Отказ);
ПараметрыЗаполнения.Автор = ПользовательПоИдентификатору(СвойстваЗадачи.AuthorID, Отказ);
РаботаСЗадачамиРеализация.СоздатьЗадачуСотрудника(ПараметрыЗаполнения, СвойстваЗадачи.TaskID, Отказ);
// в теле запроса, может быть указан url для возврата ответа ошибки
НастроитьПеренаправление(ВходящиеСвойстваЗадачи, "error_url", ЗаголовкиРезультат, КодРезультат);
Если Отказ Тогда
КодОшибки = "ВалидацияДанных";
Возврат Неопределено;
КонецЕсли;
Если СвойстваЗадачи.TaskID = Неопределено Тогда
КодОшибки = "ЗадачаНеСоздана";
Возврат Неопределено;
КонецЕсли;
// в теле запроса, может быть указан url для возврата ответа
НастроитьПеренаправление(ВходящиеСвойстваЗадачи, "success_url", ЗаголовкиРезультат, КодРезультат);
Возврат СвойстваЗадачи;
КонецФункции
На этом все)

Критикуйте, делитесь своим опытом в комментариях. Полезно - жмите плюс.
Всем успехов в проектировании http-сервисов!
Вступайте в нашу телеграмм-группу Инфостарт