К статье
Практический материал

Разработка и Конфигурация · 12 мин

1С: как проверить, выполняется ли код на клиенте или на сервере

Как в 1С определить, где выполняется код: на клиенте или на сервере. Директивы компиляции, #Если Сервер, ТекущийРежимЗапуска, типичные ошибки и примеры.

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

Проверить место выполнения кода можно двумя принципиально разными способами: на этапе компиляции — через директивы препроцессора #Если Сервер Тогда и директивы компиляции методов &НаСервере, либо во время выполнения — через функции вроде ТекущийРежимЗапуска() и вычисление выражений на стороне сервера. Выбор способа зависит от задачи: нужно ли исключить код из компиляции в конкретном контексте или просто изменить поведение алгоритма.

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

Как устроено разделение клиента и сервера в 1С

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

Модули компилируются отдельно для каждого контекста, в котором они используются. Общий модуль с флажками «Клиент (управляемое приложение)» и «Сервер» фактически компилируется дважды, и в каждой версии может присутствовать разный код. Именно поэтому проверка «где я выполняюсь» реализуется на уровне компиляции, а не только в рантайме.

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

Проверка на этапе компиляции: директивы #Если

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

#Если Сервер Тогда

// Этот код компилируется только на сервере

Запрос = Новый Запрос("ВЫБРАТЬ.. ");

#ИначеЕсли ТонкийКлиент Тогда

// Этот код компилируется только на тонком клиенте

ПоказатьПредупреждение(, "Операция выполняется на сервере");

#КонецЕсли

Ключевая особенность: код внутри «чужой» ветки вообще не попадает в скомпилированный модуль. Поэтому в серверной ветке можно безопасно использовать типы, которых нет на клиенте, и наоборот. Это единственный корректный способ работать с объектами, недоступными в данном контексте.

bsl
#Если Сервер Тогда
  Сообщить("Код выполняется на сервере");
#Иначе
  Сообщить("Код выполняется на клиенте");
#КонецЕсли

Директивы компиляции методов в управляемых формах

В модулях управляемых форм место выполнения метода задаётся явно директивой перед его объявлением. Основные варианты: &НаКлиенте, &НаСервере, &НаСервереБезКонтекста, а также &НаКлиентеНаСервереБезКонтекста для методов, компилируемых в обоих контекстах.

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

Методы &НаСервереБезКонтекста работают быстрее, потому что не сериализуют данные формы при вызове, но не имеют доступа к реквизитам формы напрямую — параметры нужно передавать явно.

Выбор директивы для метода формы

&НаКлиенте

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

&НаСервере

Нужен доступ к данным формы и запросам к базе. Медленнее из-за сериализации контекста формы.

&НаСервереБезКонтекста

Серверная логика без доступа к реквизитам формы. Быстрее, параметры передаются явно.

&НаКлиентеНаСервереБезКонтекста

Общие вспомогательные функции без обращения к данным формы и базе. Компилируется в обоих контекстах.

Проверка во время выполнения: ТекущийРежимЗапуска и другие функции

Если нужно не исключить код из компиляции, а изменить поведение в зависимости от среды запуска, используют функции времени выполнения. Основная — ТекущийРежимЗапуска(), которая возвращает значение системного перечисления: РежимЗапускаКлиентскогоПриложения.УправляемоеПриложение или ОбычноеПриложение. На сервере функция возвращает Неопределено, что само по себе является признаком серверного контекста.

Режим = ТекущийРежимЗапуска();

Если Режим = Неопределено Тогда

// Код выполняется на сервере

ИначеЕсли Режим = РежимЗапускаКлиентскогоПриложения.УправляемоеПриложение Тогда

// Управляемое приложение на клиенте

КонецЕсли;

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

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

Практический сценарий: общий модуль с проверкой контекста

Типичная задача — общий модуль, доступный и клиенту, и серверу, в котором часть логики должна выполняться только на одной стороне. Правильный шаблон выглядит так: клиентский метод определяет, что нужна серверная операция, и вызывает серверный метод отдельного общего модуля с признаком «Сервер».

Порядок действий при организации такого взаимодействия:

Порядок действий
  1. 1
    Определите, какая часть логики требует доступа к базе данных — она должна уйти на сервер
  2. 2
    Создайте общий модуль с признаком «Сервер» (и «Вызов сервера», если модуль вызывается с клиента) и вынесите туда серверную логику
  3. 3
    В клиентском коде вызывайте серверный метод, передавая только необходимые параметры простых типов
  4. 4
    Если модуль компилируется в нескольких контекстах, обрамите специфичный код директивами #Если Сервер Тогда / #Иначе / #КонецЕсли
  5. 5
    Проверьте компиляцию модуля во всех включённых контекстах через конфигуратор

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

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

Типичные ошибки при работе с контекстом

Типичные ошибки
01
Использование серверных типов (Запрос, СправочникМенеджер) в клиентском контексте без директив препроцессора — модуль не компилируется
02
Попытка показать диалог или предупреждение на сервере — ошибка выполнения, так как на сервере нет интерфейса
03
Проверка контекста через ТекущийРежимЗапуска там, где нужна директива препроцессора: код всё равно не скомпилируется на недоступной стороне
04
Множество мелких серверных вызовов из клиентского цикла вместо одного пакетного — резкое падение производительности формы
05
Отсутствие признака «Вызов сервера» у общего модуля, вызываемого с клиента — ошибка «Метод не обнаружен» или отказ в доступе к вызову

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

Ещё одна ловушка — директива &НаКлиентеНаСервереБезКонтекста. Такой метод компилируется в обоих контекстах, поэтому внутри него нельзя использовать ни серверные типы, ни клиентские диалоги без обрамления директивами #Если. Иначе ошибка проявится только в одном из режимов запуска.

Как убедиться, что проверка работает правильно

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

Контрольные точки

Как проверить результат

Проверка конфигурации в конфигураторе по всем режимам запуска
Норма

Нет ошибок компиляции модулей ни в одном контексте

Тревожный признак

Ошибки вида «Переменная не определена» или «Тип не определён» в отдельных режимах

Запуск в тонком клиенте и на сервере
Норма

Логика отрабатывает, серверные операции выполняются на сервере

Тревожный признак

Ошибки «Операция недоступна» или падение при открытии формы

Количество серверных вызовов при работе формы
Норма

Один-два вызова на пользовательское действие

Тревожный признак

Серверные вызовы в цикле, заметные задержки интерфейса

Если конфигурация типовая и дорабатывается через расширение, учитывайте, что директивы компиляции методов в расширении должны соответствовать контексту расширяемого метода. Несоответствие контекстов — частая причина того, что расширение отказывается применяться или ведёт себя непредсказуемо.

При сомнениях, где выполняется код, добавьте в начало метода временную строку с #Если Сервер Тогда Сообщить("Сервер") #Иначе Сообщить("Клиент") #КонецЕсли — это быстрее, чем искать ответ в документации по конкретному событию формы.
ПЗ
Практическое замечание

FAQ

Чем отличается #Если Сервер от проверки ТекущийРежимЗапуска() = Неопределено?

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

Почему серверный метод формы не виден из клиентского кода?

Чаще всего метод объявлен без директивы &НаСервере или &НаСервереБезКонтекста, либо это метод общего модуля без признака «Вызов сервера». Проверьте директиву компиляции метода и свойства общего модуля.

Можно ли вызвать клиентский метод с сервера?

Нет. Сервер не имеет доступа к клиентскому сеансу и интерфейсу. Взаимодействие возможно только в одну сторону: клиент вызывает сервер. Для обратной связи серверный метод возвращает результат, который клиент обрабатывает после завершения вызова.

Как определить, что код выполняется в веб-клиенте?

На этапе компиляции — директивой #Если ВебКлиент Тогда. Во время выполнения в типовых конфигурациях используют служебные функции БСП, например ОбщегоНазначенияКлиент.ЭтоВебКлиент() или аналогичные, в зависимости от версии библиотеки.

Что быстрее: &НаСервере или &НаСервереБезКонтекста?

Метод без контекста выполняется быстрее, так как платформа не передаёт на сервер и обратно данные формы. Если методу не нужны реквизиты формы, всегда выбирайте вариант без контекста и передавайте нужные значения параметрами.

Почему модуль компилируется в конфигураторе, но падает в веб-клиенте?

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

Итог

Что важно запомнить

Контекст выполнения в 1С определяется на этапе компиляции: используйте #Если Сервер Тогда для исключения кода и директивы &НаКлиенте/&НаСервере в формах. ТекущийРежимЗапуска() подходит только для изменения поведения во время выполнения и на сервере возвращает Неопределено. После изменений проверяйте компиляцию во всех режимах запуска.