Зарисовки на тему оформления кода HTTP-сервиса 4. Контроллер | infolimp.ru

Зарисовки на тему оформления кода HTTP-сервиса 4. Контроллер

15 июля 2026 · infolimp.ru

В ленте типовых HTTP-сервисов паттерн «голый роутер» давно перестал быть моветоном. Но контроллер — это не просто вызов функции, а ответственность за жизненный цикл запроса. Опытные разработчики часто путают «тонкий» контроллер с «пустым», а потом удивляются, почему при масштабировании сервис становится нагромождением дублирующейся валидации и бесконечных попыток/исключений. Разбираем, где проходит грань между архитектурной опрятностью и избыточностью, и почему в 1С контроллер — это не класс, а соглашение.

Контекст: зачем контроллер в HTTP-сервисе 1С?

Стандартный шаблон модуля HTTP-сервиса — пара десятков строк с вызовом одной экспортной процедуры. Когда таких роутов становится больше десяти, каждый обрастает копипастой: разбор заголовков, валидация JSON, обработка ошибок, логирование. Проблема — отсутствие единой точки входа, где можно перехватить исключение, нормализовать ответ и зафиксировать метрику.

Контроллер — это не объект метаданных, а концептуальная прослойка между роутером (который уже есть в конфигурации) и бизнес-логикой. Он принимает на себя:

Почему «голый роутер» — это плохо?

// Типичный код новичка — всё в одной процедуре-обработчике
Функция ОбработкаЗапроса(Запрос, Ответ, ДополнительныеПараметры) Экспорт

    ТелоСтрока = Запрос.ПолучитьТелоКакСтроку();
    СтруктураВхода = ПрочитатьЗначениеJSON(ТелоСтрока);
    Если СтруктураВхода = Неопределено Тогда
        Ответ.УстановитьТелоИзСтроки("""Ошибка""");
        Ответ.КодСостояния = 400;
        Возврат;
    КонецЕсли;

    Попытка
        Данные = Обработать(СтруктураВхода);
    Исключение
        ЗаписьЖурналаРегистрации(...);
        Ответ.УстановитьТелоИзСтроки("""Внутренняя ошибка""");
        Ответ.КодСостояния = 500;
        Возврат;
    КонецПопытки;

    Ответ.УстановитьТелоИзСтроки(ЗаписатьЗначениеJSON(Данные));
    Ответ.КодСостояния = 200;

КонецФункции

Проблема этого подхода — смешение ответственности. Обработка ошибок, парсинг, бизнес-логика — всё в одной функции. При копировании в соседний роут забывают поменять код ошибки или не обрабатывают уникальный случай, и сервис начинает «молча глотать» некорректные запросы.

Ключевой тезис: Контроллер должен быть настолько тонким, чтобы его можно было прочитать за один экран монитора, и настолько толстым, чтобы он не допускал повторения одной и той же валидации в каждом роуте. Баланс достигается не количеством кода, а структурой вызовов.

Архитектурные сценарии: «тонкий» vs «жирный» контроллер

В экосистеме 1С сложились два подхода к реализации контроллера. Ни один не является серебряной пулей — выбор зависит от сложности сервиса, количества методов и требований к тестируемости.

Сценарий 1: Контроллер как фасад

Подходит для простых CRUD-сервисов (до 20 методов). Контроллер — одна общая функция-диспетчер, которая получает имя действия из URL или заголовка и вызывает конкретный обработчик. Валидация и формат ответа вынесены в общие процедуры этого же модуля.

// Пример диспетчера в модуле HTTP-сервиса
Функция ВыполнитьДействие(ИмяДействия, Запрос, Ответ)

    // Общая валидация тела запроса
    ТелоСтрока = Запрос.ПолучитьТелоКакСтроку();
    СтруктураJSON = ПрочитатьЗначениеJSON(ТелоСтрока);
    Если ТипЗнч(СтруктураJSON) <> Тип("Структура") Тогда
        ВернутьОшибку(Ответ, 400, "Неверный формат JSON");
        Возврат;
    КонецЕсли;

    // Вызов конкретного обработчика
    Результат = Обработчики[ИмяДействия](СтруктураJSON);

    // Единый формат ответа
    Ответ.УстановитьТелоИзСтроки(ЗаписатьЗначениеJSON(Результат));
    Ответ.КодСостояния = 200;

КонецФункции

Плюсы: единая точка входа, легко подключать логирование и метрики. Минусы: модуль быстро разрастается, если в каждом обработчике своя особая логика валидации и кодов состояния.

Сценарий 2: Контроллер-команда (по действию)

Для сложных сервисов с нетривиальными бизнес-правилами (например, проверка взаимосвязей, каскадные операции). Каждый роут получает свой объект-контроллер (или набор функций) в отдельном модуле-обработчике, а общий «степлер» собирает ответ с передачей контекста ошибки.

// Модуль контроллера для конкретного метода (например, "создатьПользователя")
Функция СоздатьПользователя(Запрос, ДопПараметры) Экспорт

    // Валидация на уровне контроллера
    ТелоСтрока = Запрос.ПолучитьТелоКакСтроку();
    Данные = ПрочитатьЗначениеJSON(ТелоСтрока);

    // Проверка обязательных полей
    Если Не Данные.Свойство("Имя") Или Не Данные.Свойство("Email") Тогда
        Возврат Новый HTTPОтвет(400, "Требуются поля Имя и Email");
    КонецЕсли;

    // Вызов бизнес-логики
    Попытка
        Результат = ОбработкаПользователей.Добавить(Данные);
    Исключение
        Возврат Новый HTTPОтвет(500, КраткоеПредставлениеОшибки(ИнформацияОбОшибке()));
    КонецПопытки;

    Возврат Новый HTTPОтвет(201, ЗаписатьЗначениеJSON(Результат));

КонецФункции

Плюсы: изоляция, легко тестировать каждый контроллер отдельно. Минусы: дублирование парсинга и кода ответа — если не завести общий базовый модуль.

Ловушка: Часто разработчики в погоне за «чистотой» выносят в контроллер даже логирование и отправку уведомлений. В результате контроллер снова становится толстым, а бизнес-логика — размазанной. Правило: контроллер отвечает только за HTTP-контракт (вход/выход), всё остальное в бизнес-слое.

Практическое руководство: как построить контроллер без боли

Ниже — чек-лист, который мы выработали на нескольких промышленных интеграциях. Он не претендует на истину в последней инстанции, но проверен в бою.

Чек-лист проектирования контроллера

  1. Определите единый формат ответа. Создайте функцию СоздатьУспешныйОтвет(Код, Данные) и СоздатьОшибку(Код, Текст). Возвращайте их из всех обработчиков.
  2. Разделите чтение и валидацию. Выносите парсинг JSON в отдельную функцию с обработчиком Попытка. Возвращайте Структура (при успехе) или Неопределено.
  3. Используйте промежуточный слой контекста. Создайте структуру с полями Запрос, Ответ, ПараметрыПути, Пользователь (если авторизация). Передавайте её в каждый контроллер.
  4. Не генерируйте исключения в контроллере для бизнес-логики. Исключение — только для нештатных ситуаций (нет соединения с БД, ошибка платформы). Ожидаемые ошибки (невалидные данные, запись недоступна) обрабатывайте через коды состояния.
  5. Тестируйте изоляцию контроллера. Напишите модульные тесты, вызывая экспортные функции напрямую с искусственным контекстом. Это обнаружит «утечку» логики в контроллер.

Типичные ошибки