Зарисовки на тему оформления кода HTTP-сервиса 3. Провайдер данных | infolimp.ru

Зарисовки на тему оформления кода HTTP-сервиса 3. Провайдер данных

4 июля 2026 · infolimp.ru

В третьей части цикла о коде HTTP-сервисов автор поднимает тему, которая обычно остаётся за кадром типовых инструкций: как правильно организовать слой провайдера данных, чтобы не плодить копипасту и не терять контроль над транзакциями. Вместо абстрактных советов — конкретный шаблон с разбором граничных случаев.

Почему провайдер данных — не просто «ещё один слой»

В справке 1С и на форумах чаще всего встречаются два подхода: либо весь код обработки запроса пишут прямо в теле HTTP-сервиса, либо выносят логику в общие модули с единственной функцией «ВыполнитьЗапросКБазе». Первый вариант быстро превращается в «спагетти», второй — порождает дублирование кода при работе с разными источниками данных (регистры, документы, справочники).

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

Механика под капотом: почему без провайдера — больно

Рассмотрим типовую ситуацию: HTTP-сервис принимает JSON с данными для создания документа. Без провайдера разработчик пишет:

Функция ОбработкаЗапроса(Запрос, Ответ)
    Данные = ПрочитатьJSON(Запрос.ПолучитьТелоКакСтроку());
    Док = Документы.ЗаказПокупателя.СоздатьДокумент();
    ЗаполнитьЗначенияСвойств(Док, Данные);
    Попытка
        Док.Записать();
        Ответ.УстановитьКодСостояния(200);
    Исключение
        Ответ.УстановитьКодСостояния(500);
    КонецПопытки;
КонецФункции

Проблема: если через месяц потребуется добавить проверку остатков перед записью — код придётся менять в каждом HTTP-сервисе. Если нужно логировать все изменения — снова правка в N местах. Провайдер решает это централизованно.

Архитектура провайдера: три слоя вместо одного

Автор статьи предлагает разбить провайдер на три логических блока, каждый со своей зоной ответственности:

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

Определяет, какие данные провайдер ожидает на входе и что гарантирует на выходе. Реализуется через структуру с типизированными полями:

Функция КонтрактПровайдера() Экспорт
    Контракт = Новый Структура;
    Контракт.Вставить("ВходныеПараметры", Новый Структура("ДатаНачала, ДатаОкончания, Склад", Неопределено, Неопределено, Неопределено));
    Контракт.Вставить("ВыходныеДанные", Новый ОписаниеТипов("ТаблицаЗначений"));
    Контракт.Вставить("ПраваДоступа", "Чтение");
    Возврат Контракт;
КонецФункции
Предупреждение: Никогда не используйте Неопределено как значение по умолчанию для обязательных параметров — это маскирует ошибки на этапе компиляции. Лучше явно проверять через Заполнено() внутри провайдера.

Слой 2: Исполнитель (ядро)

Содержит реальную логику работы с данными. Здесь выполняются запросы, создаются документы, обрабатываются ошибки. Важное отличие от «лобового» подхода — исполнитель не знает про HTTP, он работает только с переданными параметрами:

Функция ВыполнитьЗапросОстатков(Параметры) Экспорт
    Результат = Новый ТаблицаЗначений;
    Результат.Колонки.Добавить("Номенклатура", Новый ОписаниеТипов("СправочникСсылка.Номенклатура"));
    Результат.Колонки.Добавить("Количество", Новый ОписаниеТипов("Число"));
    
    Запрос = Новый Запрос;
    Запрос.Текст = 
    "ВЫБРАТЬ
    |   Остатки.Номенклатура КАК Номенклатура,
    |   Остатки.КоличествоОстаток КАК Количество
    |ИЗ
    |   РегистрНакопления.ОстаткиТоваров.Остатки(, &ДатаМомента) КАК Остатки
    |ГДЕ
    |   Остатки.Склад = &Склад";
    Запрос.УстановитьПараметр("ДатаМомента", Параметры.ДатаОкончания);
    Запрос.УстановитьПараметр("Склад", Параметры.Склад);
    
    Результат = Запрос.Выполнить().Выгрузить();
    Возврат Результат;
КонецФункции

Слой 3: Адаптер (трансляция в HTTP)

Единственный слой, который знает про HTTP-контекст. Преобразует JSON/XML в структуру параметров, вызывает исполнителя, сериализует ответ:

Функция ОбработатьЗапросОстатков(ЗапросHTTP, ОтветHTTP) Экспорт
    Параметры = Новый Структура;
    Параметры.Вставить("ДатаНачала", Неопределено);
    Параметры.Вставить("ДатаОкончания", ТекущаяДата());
    Параметры.Вставить("Склад", Неопределено);
    
    // Заполняем из тела запроса
    Тело = ЗапросHTTP.ПолучитьТелоКакСтроку();
    Если ПустаяСтрока(Тело) Тогда
        ОтветHTTP.УстановитьКодСостояния(400);
        ОтветHTTP.УстановитьТелоИзСтроки("Не передан JSON");
        Возврат;
    КонецЕсли;
    
    ЧтениеJSON = Новый ЧтениеJSON;
    ЧтениеJSON.УстановитьСтроку(Тело);
    Попытка
        Данные = ПрочитатьJSON(ЧтениеJSON);
    Исключение
        ОтветHTTP.УстановитьКодСостояния(400);
        ОтветHTTP.УстановитьТелоИзСтроки("Ошибка разбора JSON: " + КраткоеПредставлениеОшибки(ИнформацияОбОшибке()));
        Возврат;
    КонецПопытки;
    
    Если Данные.Свойство("Дата") Тогда
        Параметры.ДатаОкончания = Данные.Дата;
    КонецЕсли;
    Если Данные.Свойство("Склад") Тогда
        Параметры.Склад = Справочники.Склады.НайтиПоКоду(Данные.Склад);
    КонецЕсли;
    
    Результат = ВыполнитьЗапросОстатков(Параметры);
    
    // Сериализация ответа
    ЗаписьJSON = Новый ЗаписьJSON;
    Ответ = Новый Массив;
    Для Каждого Строка Из Результат Цикл
        Ответ.Добавить(Новый Структура("Номенклатура, Количество", Строка.Номенклатура.Наименование, Строка.Количество));
    КонецЦикла;
    ЗаписьJSON.УстановитьСтроку();
    ЗаписатьJSON(ЗаписьJSON, Ответ);
    ОтветHTTP.УстановитьКодСостояния(200);
    ОтветHTTP.УстановитьТелоИзСтроки(ЗаписьJSON.Закрыть(), "application/json");
КонецФункции

Нетривиальная ловушка: транзакции и блокировки

Самая частая ошибка при реализации провайдера — попытка управлять транзакцией внутри исполнителя. Представьте: HTTP-сервис вызывает два провайдера последовательно — сначала запись документа, потом обновление остатков. Если каждый провайдер открывает свою транзакцию, при сбое во втором первый уже зафиксирован. Результат — «битые» данные.

Правило: Транзакция должна открываться ровно один раз — в адаптере HTTP-сервиса. Исполнитель провайдера работает в контексте уже открытой транзакции и никогда не вызывает НачатьТранзакцию() сам.

Реализация безопасного вызова

Функция ОбработатьКомплексныйЗапрос(ЗапросHTTP, ОтветHTTP) Экспорт
    НачатьТранзакцию();
    Попытка
        // Вызов первого провайдера
        Провайдер1.ВыполнитьЗаписьДокумента(ПараметрыДокумента);
        // Вызов второго провайдера
        Провайдер2.ОбновитьОстатки(ПараметрыОстатков);
        ЗафиксироватьТранзакцию();
        ОтветHTTP.УстановитьКодСостояния(200);
    Исключение
        ОтменитьТранзакцию();
        ОтветHTTP.УстановитьКодСостояния(500);
        ОтветHTTP.УстановитьТелоИзСтроки(КраткоеПредставлениеОшибки(ИнформацияОбОшибке()));
    КонецПопытки;
КонецФункции

Типичные ошибки и чек-лист

Ошибка 1: Смешивание логики доступа и представления

Провайдер не должен возвращать готовый JSON — его задача вернуть данные в виде таблицы значений или структуры. Сериализация — дело адаптера.

Ошибка 2: Игнорирование прав доступа

Если провайдер вызывается из HTTP-сервиса, который работает в привилегированном режиме, проверка прав должна быть явной внутри исполнителя:

Если Не Пользователи.ЕстьПраво("Чтение", Метаданные.РегистрыНакопления.ОстаткиТоваров) Тогда
    ВызватьИсключение "Недостаточно прав для чтения остатков";
КонецЕсли;

Чек-лист «Провайдер данных»

Что делать прямо сейчас

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

Попробовать НОПик →