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

26.06.26

Интеграция - WEB-интеграция

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

Для кого: для начинающих разработчиков, кто еще не сформировал свой архитектурный стиль; для опытных, кто может увидеть недочеты и поделиться своими идеями и приемами в комментариях.

Хотел бы я написать: «Меня зовут Шляхин Сергей, разработал столько-то сервисов, помог бизнесу таким-то компаниям». К сожалению, пока не могу). В 1С долго, но с большим перерывом, уходил в смежную область, не сложилось, поэтому возвращаюсь.

Пару месяцев назад прошел замечательный курс (автор: Матвей Серегин), который дал много идей, было время все осмыслить. На основе синтетического примера из курса: «Управление задачами», представляю очередной пет-проект, делюсь результатом систематизации полученных знаний.

 

Слои

На мой взгляд для работы сервиса необходимо минимум 4 модуля, условно назвал так: Модуль сервиса, Контроллер, Реализация, Валидация.

Пусть наш сервис обслуживает выдуманный документ «Задача Сотрудника», тогда модули будут называться РаботаСЗадачами, РаботаСЗадачамиHTTP, РаботаСЗадачамиРеализация, ВалидацияДанных.

 

Модуль Описание
РаботаСЗадачами

Входная точка сервиса, максимально типизированный код методов + обработка ошибок и ответы «матрешки». Общается только с модулем «РаботаСЗадачамиHTTP», ожидает получить требуемые данные, настроенные заголовки или известный КодОшибки.

 

РаботаСЗадачамиHTTP

Мозг и главный контроллер сервиса.

  1. Получает задание от «РаботаСЗадачами».

  2. Основная бизнес-логика сервиса здесь.

  3. Дает команды «ВалидацияДанных» (знает о его возможностях), чтобы проверить входные данные

  4. Преобразует их (входные данные) в понятный для «РаботаСЗадачамиРеализация» вид, чтобы получить данные из БД.

  5. Готовит данные из БД в необходимый формат для передачи или возвращает КодОшибки в «РаботаСЗадачами».

 

РаботаСЗадачамиРеализация

Доступ к БД (вернее необходимой для сервиса части) и подсистемам конфигурации. Ничего не знает о сервисе, почти не имеет никакой  логики, предоставляет данные и все.

 

ВалидацияДанных

Самый независимый модуль, программный интерфейс которого заточен на проверку входящих значений по определенным правилам или схемам. Ничего не знает о сервисе, БД, может быть использован любой подсистемой ИС.

 

Обозначили границы, закрепили функции - разрабатывать, тестировать можно независимо.

Модифицировать и искать ошибки тоже легко.

 

Модуль сервиса РаботаСЗадачами

  1. Типизированный код методов

  2. Обработка ошибок

  3. Ответы матрешки

 

Типизированный код методов

Разберем на примерах:

Функция СоздатьЗадачу(Запрос)
    
    // Область блокировок: условие и реакция ответ
    
    // примеры:
    // - требуется обновление
    Если РаботаСЗадачамиHTTP.ЗаблокированоОбновлением() Тогда Возврат ОтветЗаблокированоОбновлением() КонецЕсли;
    
    // - превышено число запросов
    Заголовки = Новый Соответствие; // результирующие заголовки
    Если РаботаСЗадачамиHTTP.ПревышеноЧислоЗапросов(Заголовки) Тогда
        
        Возврат ОтветПревышеноЧислоЗапросов(Заголовки)
    КонецЕсли;
    
    // - идентификация пользователя, см. ЗУП, сервис: ЗагрузкаКандидатов
    
    КодОшибки = ""; Код = 201; // результирующие код ошибки и код состояния
    
    // Получение данных через попытку
    Попытка
        
        // ожидаем свойства новой задачи, Код, Заголовки или КодОшибки
        НоваяЗадача = РаботаСЗадачамиHTTP.НоваяЗадачаСотрудника(Запрос, КодОшибки, Заголовки, Код)
    Исключение
        
        // Обработка исключения
        Возврат ОтветВнутренняяОшибка(ИнформацияОбОшибке())
    КонецПопытки;
    
    // Обработка ошибки
    Если КодОшибки <> "" Тогда Возврат ОтветТипизированнаяОшибка(КодОшибки, Заголовки) КонецЕсли;
    
    // Область результата, разбор состояний:
        
    // - переадресация - реакция ответ
    Если Код = 303 Тогда Возврат НовыйОтвет(Код, Заголовки) КонецЕсли;
    
    // - ожидаемое состояние - реакция ответ
    Возврат Ответ(, "Новая задача создана", НоваяЗадача, Заголовки, Код)
КонецФункции
 

Пример попроще, без комментариев:

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

И так оформляем каждый метод

 

Обработка ошибок

Важно разработать "говорящую" систему ошибок для сервиса. Такой минимальный набор в моем сервисе:

#Область УправлениеОшибками

Функция НоваяОшибка(Описание, Код, ЕстьДетали = Ложь)

    Возврат Новый Структура("Описание,Код,ЕстьДетали", Описание, Код, ЕстьДетали)
КонецФункции

Функция ТипизированныеОшибки()
    
    Ошибки = Новый Соответствие;
    
    // ошибки запроса
    Ошибки.Вставить("ТипТела", НоваяОшибка("Тело запроса не верного типа", 400));
    Ошибки.Вставить("ФорматТела", НоваяОшибка("Нарушен формат тела", 400));
    Ошибки.Вставить("ЗадачаНеНайдена", НоваяОшибка("Задача не найдена", 404));
    Ошибки.Вставить("ПустоеТелоЗадания", НоваяОшибка("Пустое тело задания", 400));
    Ошибки.Вставить("Разделитель", НоваяОшибка("Не указан разделитель тела запроса", 400));
    Ошибки.Вставить("НетФайлов", НоваяОшибка("Тело запроса не содержит файлов", 400));
    Ошибки.Вставить("РазныеВладельцы", НоваяОшибка("Файл не принадлежит указанной задаче", 404));
    
    // ошибки сервиса
    Ошибки.Вставить("ЗадачаНеСоздана", НоваяОшибка("Задача не создана", 500));
    Ошибки.Вставить("ЗаданиеНеСоздано", НоваяОшибка("Задание не создано", 500));
    
    // ошибки, у которых есть какие-то детали
    Ошибки.Вставить("ВалидацияДанных", НоваяОшибка("Не пройдена валидация входных данных", 400, Истина));
    
    Возврат Ошибки
КонецФункции

// Механизм сбора ошибок, использую при валидации входных данных
Функция СписокСообщенийПользователю()
    
    Список = Новый Массив;
    
    Сообщения = ПолучитьСообщенияПользователю(Истина);
    Для Каждого Сообщение Из Сообщения Цикл
        
        Список.Добавить(Сообщение.Текст)
    КонецЦикла;
    
    Возврат Список;
КонецФункции

// Некоторые ошибки желательно зафиксировать для разбора
Процедура СделатьЗаписьЖРОбОшибке(ИнформацияОбОшибке)

    ИмяСобытия = "РаботаСЗадачамиHTTP.Ошибка";
    Уровень = УровеньЖурналаРегистрации.Ошибка;
    Объект = Метаданные.HTTPСервисы.РаботаСЗадачами;
    
    Комментарий = "";
    Если ТипЗнч(ИнформацияОбОшибке) = Тип("ИнформацияОбОшибке") Тогда
        
        Комментарий = ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке)
    Иначе
        
        Комментарий = Строка(ИнформацияОбОшибке)
    КонецЕсли;
    
    ЗаписьЖурналаРегистрации(ИмяСобытия, Уровень, Объект,, Комментарий);
КонецПроцедуры

#КонецОбласти

 

Ответы матрешки

Минимальный ответ требует код состояния и заголовки:

Функция НовыйОтвет(Код, Заголовки)

    Ответ = Новый HTTPСервисОтвет(Код);
    
    Если ТипЗнч(Заголовки) = Тип("Соответствие") Тогда
        
        Ответ.Заголовки = Заголовки;
    КонецЕсли;
    
    // место, где можно обогатить каждый ответ специальными заголовками, например CORS
    
    Возврат Ответ
КонецФункции

Будем использовать как самую маленькую матрешку для других вариантов.

Базовый ответ с телом JSON как самый распространённый транспорт:

Функция Ответ(Успех = Истина, Описание = "", ТелоДанные = Неопределено, Заголовки = Неопределено, Код = 200)

    Ответ = НовыйОтвет(Код, Заголовки);
    
    Если ПустаяСтрока(Описание) Тогда Возврат Ответ КонецЕсли;
    
    Результат = Новый Структура;
    Результат.Вставить("result", Успех);
    Результат.Вставить("description", Описание);
    
    Если ТелоДанные <> Неопределено Тогда
        
        Ключ = ?(Успех, "data", "errors");
        Результат.Вставить(Ключ, ТелоДанные);
    КонецЕсли;
    
    Ответ.Заголовки.Вставить("Content-Type", "application/json");
    Ответ.УстановитьТелоИзСтроки(ЗаписатьЗначениеJSON(Результат));
    
    Возврат Ответ
КонецФункции

Использую как для успешных ответов, так и для ответов с информацией об ошибке.

Другие варианты с телом:

#Область ОтветыСТелом

Функция ОтветТекст(ТелоДанные, Заголовки = Неопределено, Код = 200, ТипСодержимого = "text/plain")

    Ответ = НовыйОтвет(Код, Заголовки);
    
    Ответ.Заголовки.Вставить("Content-Type", ТипСодержимого);
    Ответ.УстановитьТелоИзСтроки(ТелоДанные);
    
    Возврат Ответ
КонецФункции

Функция ОтветДвоичныеДанные(ТелоДанные, Заголовки = Неопределено, Код = 200, ТипСодержимого = "application/octet-stream")
    
    Ответ = НовыйОтвет(Код, Заголовки);
    
    Ответ.Заголовки.Вставить("Content-Type", ТипСодержимого);
    Ответ.УстановитьТелоИзДвоичныхДанных(ТелоДанные);
    
    Возврат Ответ
КонецФункции

#КонецОбласти

Еще одна матрешка - ошибки сервиса:

#Область ОшибкиСервиса

Функция ОтветОшибкаСервиса(Описание = "Ошибка сервиса", Предложение = Неопределено, Код = 500)
    
    Возврат Ответ(Ложь, Описание, Предложение,, Код)
КонецФункции

Функция ОтветЗаблокированоОбновлением()
    
    Описание = "Заблокировано обновлением";
    Предложение = "Сервис не доступен, попробуйте позже";
    
    Возврат ОтветОшибкаСервиса(Описание, Предложение, 503)
КонецФункции

Функция ОтветВнутренняяОшибка(ИнформацияОбОшибке)
    
    СделатьЗаписьЖРОбОшибке(ИнформацияОбОшибке);
    
    Возврат ОтветОшибкаСервиса("Внутренняя ошибка")
КонецФункции

#КонецОбласти

И специальные:

Функция ОтветПревышеноЧислоЗапросов(Заголовки)
    
    Возврат Ответ(Ложь, "Превышено число запросов", Неопределено, Заголовки, 429)
КонецФункции

Функция ОтветТипизированнаяОшибка(КодОшибки, Заголовки, ПараметрЗапроса = "?errorText=")
    
    ОписаниеОшибки = ТипизированныеОшибки().Получить(КодОшибки);
    Если ОписаниеОшибки = Неопределено Тогда
        
        ОписаниеОшибки = НоваяОшибка("Не типизированная ошибка сервиса", 500);
        
        СделатьЗаписьЖРОбОшибке(СтрШаблон("Не типизированная ошибка ""%1""", КодОшибки));
    КонецЕсли;
    
    Детали = Неопределено;
    Если ОписаниеОшибки.ЕстьДетали Тогда
        
        Детали = СтрСоединить(СписокСообщенийПользователю(), Символы.ПС);
    КонецЕсли;
    
    Location = Заголовки.Получить("Location"); // если указан, то ошибку передать через параметр, с кодом 303
    
    Если Location <> Неопределено Тогда
        
        ТекстОшибки = ОписаниеОшибки.Описание + ?(ОписаниеОшибки.ЕстьДетали, ":" + Символы.ПС + Детали, "");
        ТекстОшибки = КодироватьСтроку(ТекстОшибки, СпособКодированияСтроки.КодировкаURL);
        
        Заголовки.Вставить("Location", Location + ПараметрЗапроса + ТекстОшибки);
        
        Возврат НовыйОтвет(303, Заголовки);
    КонецЕсли;
    
    Возврат Ответ(Ложь, ОписаниеОшибки.Описание, Детали, Заголовки, ОписаниеОшибки.Код)
КонецФункции

Ответ с типизированной ошибкой:

  • разбирает известные ошибки и выводит в ответе их описание, детали, устанавливает соответствующий код;

  • если ошибка не найдена, то об этом будет сделана запись в журнале регистрации;

  • показан вариант, как можно обработать сразу несколько ошибок (Детали);

  • включает возможность представить ответ в HTML-форме, через код и заголовки переадресации.

 

Замечания

  1. Подготовка заголовков ответов разделена:

    1. «РаботаСЗадачамиHTTP» готовит специфические для бизнес-логики;

    2. «РаботаСЗадачами» общие для всех ответов (CORS), тип содержимого, а также те, которые требуют включить текст ошибки в значение (показ ответа в HTML-форме)

  2. Ввод нескольких вариантов методов для подготовки ответов обусловлен удобством и повышением читаемости кода. На примере ошибок сервиса, в методы зашиты или код, или дополнительные описание или функционал.

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

Хочу верить, что данный шаблон слоя можно переиспользовать от сервиса к сервису.

 

На сегодня все. Если тема интересна, понравился формат и стоит продолжать раскрывать слои, то дайте знать в комментариях или лайками).

 

ПродолжениеВалидация данных

Вступайте в нашу телеграмм-группу Инфостарт

HTTP-сервис Архитектура Слои Обработка ошибок

Вы можете заказать платную адаптацию этой статьи под ваши задачи на «Бирже заказов».

  • 0% комиссии — оплата напрямую исполнителю;
  • Исполнители любого масштаба — от отдельных специалистов до команд под проект;
  • Прямой обмен контактами между заказчиком и исполнителем;
  • Безопасная сделка — при необходимости;
  • Рейтинги, кейсы и прозрачная система откликов.

См. также

Сайты и интернет-магазины WEB-интеграция Системный администратор Программист Пользователь 1С:Предприятие 8 1C:Бухгалтерия 1С:Управление торговлей 11 Автомобили, автосервисы Россия Управленческий учет Платные (руб)

Интеграционный модуль обмена между конфигурацией Альфа Авто 5 и Альфа Авто 6 и порталом AUTOCRM / LOGICSTARS. Данный модуль универсален. Позволяет работать с несколькими обменами AUTOCRM / LOGICSTAR разных брендов в одной информационной базе в ручном и автоматическом режиме.

42700 руб.

03.08.2020    24694    38    26    

29

WEB-интеграция Программист 1С:Предприятие 8 1С:Бухгалтерия 3.0 Бытовые услуги, сервис Платные (руб)

Расширение для автоматизации передачи данных между сервисом Vetmanager с 1С: Бухгалтерия 3.0. Решение позволяет загружать документы и справочники из Ветменеджер в 1С:Бухгалтерию, сокращая время на ручной ввод данных и минимизируя ошибки.

24000 руб.

02.02.2021    23401    73    52    

44

WEB-интеграция Программист Бизнес-аналитик 1С:Предприятие 8 1С:ERP Управление предприятием 2 1С:Бухгалтерия 3.0 1С:Управление торговлей 11 1С:Комплексная автоматизация 2.х 1С:Управление нашей фирмой 3.0 1С:Розница 3.0 Оптовая торговля, дистрибуция, логистика ИТ-компания Платные (руб)

Модуль "Экспортер" — это расширение для 1С, предназначенное для автоматизации процессов выгрузки данных. Оно позволяет эффективно извлекать, преобразовывать и передавать данные из систем 1С в интеграционную платформу Spot2D. Подсистема упрощает настройку, снижает количество ручных операций и обеспечивает удобный контроль данных.

17568 руб.

20.12.2024    6663    28    4    

30

WEB-интеграция Системный администратор Программист Пользователь 1С:Предприятие 8 1C:Бухгалтерия 1С:Бухгалтерия 3.0 1С:Управление торговлей 11 Автомобили, автосервисы Россия Управленческий учет Платные (руб)

Интеграционный модуль обмена по API между конфигурацией 1С:Альфа-Авто 6 и порталом LogicStar. Позволяет работать с несколькими обменами LogicStar разных брендов (CHERY, OMODA, JAECOO, EXEED, TENET) в одной информационной базе в ручном и автоматическом режиме. Поддерживается выгрузка заказ-нарядов, реализаций товаров и товарных остатков.

20740 руб.

13.05.2025    2348    2    0    

5
Комментарии
Подписаться на ответы Инфостарт бот Сортировка: Древо развёрнутое
Свернуть все
1. gybson 13 26.06.26 12:35 Сейчас в теме
Не надо в "Попытка" так много тянуть лишнего. Если исключение вызывает только одна строка "РаботаСЗадачамиHTTP.НоваяЗадачаСотрудника(Запрос, КодОшибки, Заголовки, Код)", то только её там и оставьте.
2. SAS1C 38 26.06.26 14:06 Сейчас в теме
(1) Согласен. Спасибо, что заметили. Изменил, перенес "лишнее" в область результат
Для отправки сообщения требуется регистрация/авторизация