Как определить в коде 1С, что вызов пришел через интерфейс oData?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При разработке механизмов интеграции и автоматизации в системе 1С:Предприятие часто возникает задача разделения логики выполнения кода в зависимости от того, кто является инициатором вызова. Например, при интерактивной работе пользователя в документе должны формироваться одни движения, а при загрузке данных через стандартный интерфейс oData — другие (или вовсе отключаться). В этой статье мы подробно разберем, почему для этой задачи нельзя использовать стандартные директивы препроцессора и как правильно идентифицировать oData-запросы программным способом.

Архитектурное различие: почему не существует директивы препроцессора для oData

Разберем фундаментальный механизм работы платформы. Многие разработчики привыкли использовать конструкции типа #Если ВнешнееСоединение Тогда или #Если Сервер Тогда. Однако важно понимать, что директивы препроцессора отрабатывают в момент компиляции модуля, а не в момент его исполнения (runtime).

Проанализируем ситуацию: когда сервер 1С компилирует программный код (например, МодульОбъекта документа), он создает исполняемый двоичный код для контекста Сервер. На этом этапе платформа не знает и не может знать, какой именно запрос вызовет этот код через секунду — поступит ли он от тонкого клиента, от регламентного задания или через REST API (oData). Для всех этих случаев используется один и тот же скомпилированный блок кода с директивой #Если Сервер Тогда.

Следовательно, выделить oData на уровне препроцессора невозможно, так как это потребовало бы наличия отдельного контекста компиляции, которого для oData в архитектуре 1С не предусмотрено. oData — это не отдельный вид клиента (как COM-соединение), а технология доступа к данным, работающая внутри серверного контекста.

Программный метод идентификации через сеанс информационной базы

Выяснив, что директивы нам не помогут, рассмотрим наиболее надежный и правильный способ определения контекста во время выполнения кода. Для этого мы будем использовать свойства текущего сеанса информационной базы. Рассмотрим подробнее функцию ПолучитьТекущийСеансИнформационнойБазы().

У объекта сеанса есть свойство ИмяПриложения, которое однозначно идентифицирует тип соединения. Для стандартного интерфейса oData платформа всегда устанавливает значение этого свойства в строку "OData".

Разберем пример кода, который можно разместить в обработчике ПередЗаписью модуля объекта:


Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
    
    // Определяем, вызвано ли выполнение через oData
    ТекущийСеанс = ПолучитьТекущийСеансИнформационнойБазы();
    
    Если ТекущийСеанс.ИмяПриложения = "OData" Тогда
        // Логика специально для oData
        // Например, отключаем проведение или заполняем доп. реквизиты
        ЭтотОбъект.ДополнительныеСвойства.Вставить("ВыполненоЧерезOData", Истина);
    Иначе
        // Стандартная логика для пользователей и других сервисов
    КонецЕсли;

КонецПроцедуры

Особенности VRS-сессий и управления сеансами

Проанализируем, как oData взаимодействует с сессиями на сервере 1С. Запросы к стандартному интерфейсу используют так называемые VRS-соединения (Virtual Resource System). В консоли кластера серверов такие соединения отображаются отдельно.

Важный момент для разработчика заключается в том, что oData-запросы по своей природе являются stateless (не хранят состояние). Это означает, что на каждый HTTP-запрос платформа может выделять сеанс из пула, выполнять код и возвращать его обратно. Поэтому попытки использовать ПараметрыСеанса для фиксации признака "это oData" могут работать нестабильно, если вы не инициализируете их в специализированном модуле Сессия или в обработчике УстановкаПараметровСеанса.

Если вы решите использовать ПараметрыСеанса, логика должна быть следующей:


Процедура УстановкаПараметровСеанса(ТребуемыеПараметры)
    
    ИмяПриложения = ПолучитьТекущийСеансИнформационнойБазы().ИмяПриложения;
    
    Если ИмяПриложения = "OData" Тогда
        ПараметрыСеанса.ЭтоИнтерфейсOData = Истина;
    КонецЕсли;
    
КонецПроцедуры

Сравнение с внешним соединением и HTTP-сервисами

Часто возникает путаница: почему oData не считается внешним соединением? Давайте выясним причину. Внешнее соединение (COM) — это отдельный технологический стек, который имеет свой собственный контекст компиляции (тот самый #Если ВнешнееСоединение). Он не использует веб-сервер для передачи команд.

oData же работает поверх протокола HTTP через публикацию на веб-сервере (Apache или IIS). В этом плане она ближе к самописным HTTP-сервисам. Однако есть различие в ИмяПриложения:

  1. OData — для стандартного (автоматического) интерфейса REST.
  2. WebServerExtension — часто устанавливается для запросов к произвольным HTTP-сервисам конфигурации.

Посмотрим на пример, как можно универсально проверять "неинтерактивность" вызова, если мы хотим объединить проверку на oData и фоновые задания:


Функция ЭтоРоботизированныйВызов() Экспорт
    
    ИмяПриложения = ПолучитьТекущийСеансИнформационнойБазы().ИмяПриложения;
    
    Возврат (ИмяПриложения = "OData") 
        ИЛИ (ИмяПриложения = "BackgroundJob") // Фоновое задание
        ИЛИ (ИмяПриложения = "SystemBackgroundJob"); // Системное фоновое задание

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

Практический кейс: Отключение движений при загрузке через oData

Вернемся к задаче, озвученной в обсуждении: нужно лишать документ возможности делать движения, если он пришел через oData. Поскольку мы выяснили, что директив препроцессора нет, мы используем проверку в ОбработкаПроведения.

Важно: Платформа 1С при выполнении POST или PATCH запроса через oData может автоматически вызывать проведение, если в URL передан соответствующий параметр. Чтобы заблокировать это поведение на уровне кода, используйте следующий подход:


Процедура ОбработкаПроведения(Отказ, РежимПроведения)
    
    Если ПолучитьТекущийСеансИнформационнойБазы().ИмяПриложения = "OData" Тогда
        // Если мы не хотим, чтобы oData-интерфейс мог проводить документы
        Сообщить("Проведение документов через oData запрещено правилами системы");
        Отказ = Истина;
        Возврат;
    КонецЕсли;
    
    // Основная логика проведения...
    
КонецПроцедуры

Резюме и рекомендации

Подведем итоги нашего анализа:

  1. Директивы препроцессора (#Если...) для oData не существует, так как это рантайм-свойство сеанса, а не контекст компиляции модуля.
  2. Единственный надежный способ определения oData — проверка ПолучитьТекущийСеансИнформационнойБазы().ИмяПриложения = "OData".
  3. Для оптимизации производительности (чтобы не вызывать получение сеанса в каждой строке кода) рекомендуется кэшировать этот признак в ПараметрыСеанса при старте сессии.
  4. Всегда учитывайте, что код под oData исполняется на сервере, поэтому все серверные ограничения (отсутствие доступа к файловой системе клиента, отсутствие диалоговых окон) сохраняются.

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

← На главную