Разработка и Конфигурация · 12 мин
1С: как проверить, выполняется ли код на клиенте или на сервере
Как в 1С определить, где выполняется код: на клиенте или на сервере. Директивы компиляции, #Если Сервер, ТекущийРежимЗапуска, типичные ошибки и примеры.
В клиент-серверной архитектуре 1С один и тот же модуль может компилироваться и исполняться в разных контекстах: на тонком клиенте, веб-клиенте, толстом клиенте или на сервере. От того, где выполняется код, зависит, какие объекты доступны, можно ли обратиться к базе данных напрямую и допустим ли интерактивный диалог с пользователем. Ошибка в определении контекста — одна из самых частых причин сообщений вида «Процедура или функция с указанным именем не определена» и «Операция не доступна на сервере».
Проверить место выполнения кода можно двумя принципиально разными способами: на этапе компиляции — через директивы препроцессора #Если Сервер Тогда и директивы компиляции методов &НаСервере, либо во время выполнения — через функции вроде ТекущийРежимЗапуска() и вычисление выражений на стороне сервера. Выбор способа зависит от задачи: нужно ли исключить код из компиляции в конкретном контексте или просто изменить поведение алгоритма.
Ниже разобраны оба подхода с примерами, ограничениями и типичными ошибками, а также способы убедиться, что проверка написана правильно.
Как устроено разделение клиента и сервера в 1С
Платформа 1С:Предприятие исполняет код в нескольких средах: тонкий клиент, веб-клиент, толстый клиент (обычное и управляемое приложение) и сервер 1С. У каждой среды свой набор доступных типов и объектов. Например, на сервере доступны запросы к базе данных и прямое изменение объектов, но недоступны диалоговые окна и модальные методы ввода. На клиенте, наоборот, доступен интерфейс, но нет прямого доступа к данным.
Модули компилируются отдельно для каждого контекста, в котором они используются. Общий модуль с флажками «Клиент (управляемое приложение)» и «Сервер» фактически компилируется дважды, и в каждой версии может присутствовать разный код. Именно поэтому проверка «где я выполняюсь» реализуется на уровне компиляции, а не только в рантайме.
В обычных формах (толстый клиент) разделения контекстов внутри формы нет — весь код выполняется на клиенте, а серверный вызов делается через общие модули. В управляемых формах разделение явное: каждый метод помечается директивой, определяющей место его компиляции и исполнения.
Проверка на этапе компиляции: директивы #Если
Директивы препроцессора позволяют включить или исключить фрагмент кода при компиляции модуля в конкретном контексте. Доступные инструкции: Сервер, Клиент, ТонкийКлиент, ВебКлиент, ТолстыйКлиентОбычноеПриложение, ТолстыйКлиентУправляемоеПриложение, ВнешнееСоединение, МобильныйКлиент и другие.
#Если Сервер Тогда
// Этот код компилируется только на сервере
Запрос = Новый Запрос("ВЫБРАТЬ.. ");
#ИначеЕсли ТонкийКлиент Тогда
// Этот код компилируется только на тонком клиенте
ПоказатьПредупреждение(, "Операция выполняется на сервере");
#КонецЕсли
Ключевая особенность: код внутри «чужой» ветки вообще не попадает в скомпилированный модуль. Поэтому в серверной ветке можно безопасно использовать типы, которых нет на клиенте, и наоборот. Это единственный корректный способ работать с объектами, недоступными в данном контексте.
#Если Сервер Тогда
Сообщить("Код выполняется на сервере");
#Иначе
Сообщить("Код выполняется на клиенте");
#КонецЕслиДирективы компиляции методов в управляемых формах
В модулях управляемых форм место выполнения метода задаётся явно директивой перед его объявлением. Основные варианты: &НаКлиенте, &НаСервере, &НаСервереБезКонтекста, а также &НаКлиентеНаСервереБезКонтекста для методов, компилируемых в обоих контекстах.
Чтобы понять, где выполняется конкретный метод, достаточно посмотреть на его директиву. Проблемы возникают, когда из клиентского метода нужны данные, доступные только на сервере: тогда создают отдельный серверный метод и вызывают его из клиентского. Каждый такой вызов — это обращение к серверу, поэтому их количество стоит минимизировать, собирая данные пакетно.
Методы &НаСервереБезКонтекста работают быстрее, потому что не сериализуют данные формы при вызове, но не имеют доступа к реквизитам формы напрямую — параметры нужно передавать явно.
Выбор директивы для метода формы
Интерактивная логика, работа с интерфейсом, файловые операции на стороне пользователя. Нет доступа к базе данных.
Нужен доступ к данным формы и запросам к базе. Медленнее из-за сериализации контекста формы.
Серверная логика без доступа к реквизитам формы. Быстрее, параметры передаются явно.
Общие вспомогательные функции без обращения к данным формы и базе. Компилируется в обоих контекстах.
Проверка во время выполнения: ТекущийРежимЗапуска и другие функции
Если нужно не исключить код из компиляции, а изменить поведение в зависимости от среды запуска, используют функции времени выполнения. Основная — ТекущийРежимЗапуска(), которая возвращает значение системного перечисления: РежимЗапускаКлиентскогоПриложения.УправляемоеПриложение или ОбычноеПриложение. На сервере функция возвращает Неопределено, что само по себе является признаком серверного контекста.
Режим = ТекущийРежимЗапуска();
Если Режим = Неопределено Тогда
// Код выполняется на сервере
ИначеЕсли Режим = РежимЗапускаКлиентскогоПриложения.УправляемоеПриложение Тогда
// Управляемое приложение на клиенте
КонецЕсли;
Дополнительно используются функции ЭтоВебКлиент() из состава БСП (ОбщегоНазначения.ЭтоВебКлиент() в типовых конфигурациях) и ИнформацияОСреде() для получения сведений о платформе. В типовых конфигурациях на базе БСП принято определять тип клиента через ОбщегоНазначенияКлиент.ТипКлиента() или аналогичные служебные функции — они инкапсулируют проверки и упрощают сопровождение.
Важное ограничение: проверка во время выполнения не защищает от ошибок компиляции. Если в клиентском контексте использован тип, которого там нет, модуль не скомпилируется независимо от того, что ветка кода «никогда не выполнится». Поэтому недоступные в контексте типы всегда скрывают за директивами препроцессора.
Практический сценарий: общий модуль с проверкой контекста
Типичная задача — общий модуль, доступный и клиенту, и серверу, в котором часть логики должна выполняться только на одной стороне. Правильный шаблон выглядит так: клиентский метод определяет, что нужна серверная операция, и вызывает серверный метод отдельного общего модуля с признаком «Сервер».
Порядок действий при организации такого взаимодействия:
- 1Определите, какая часть логики требует доступа к базе данных — она должна уйти на сервер
- 2Создайте общий модуль с признаком «Сервер» (и «Вызов сервера», если модуль вызывается с клиента) и вынесите туда серверную логику
- 3В клиентском коде вызывайте серверный метод, передавая только необходимые параметры простых типов
- 4Если модуль компилируется в нескольких контекстах, обрамите специфичный код директивами #Если Сервер Тогда / #Иначе / #КонецЕсли
- 5Проверьте компиляцию модуля во всех включённых контекстах через конфигуратор
Признак «Вызов сервера» у общего модуля обязателен, если его методы вызываются с клиента: без него попытка обращения завершится ошибкой. При этом включать этот признак «на всякий случай» не стоит — он расширяет поверхность серверных вызовов и требует внимания к безопасности: клиент может передать произвольные параметры.
Для отладки, где фактически выполняется код, удобно временно выводить сообщение с указанием контекста через директивы препроцессора — в каждой ветке своё сообщение. По тому, какое сообщение появилось, сразу видно, в какой среде скомпилировался модуль.
Типичные ошибки при работе с контекстом
Отдельная частая ситуация — перенос кода из обычных форм в управляемые. В обычном приложении разработчик привык, что запрос к базе можно выполнить прямо из модуля формы. В управляемой форме тот же код требует выноса в серверный метод, иначе форма просто не откроется.
Ещё одна ловушка — директива &НаКлиентеНаСервереБезКонтекста. Такой метод компилируется в обоих контекстах, поэтому внутри него нельзя использовать ни серверные типы, ни клиентские диалоги без обрамления директивами #Если. Иначе ошибка проявится только в одном из режимов запуска.
Как убедиться, что проверка работает правильно
После внесения изменений проверьте компиляцию всех затронутых модулей в каждом контексте, где они используются. В конфигураторе для этого есть проверка конфигурации с указанием режимов: тонкий клиент, веб-клиент, сервер, толстый клиент. Ошибки компиляции в «чужом» контексте всплывают именно на этом этапе.
Как проверить результат
Нет ошибок компиляции модулей ни в одном контексте
Ошибки вида «Переменная не определена» или «Тип не определён» в отдельных режимах
Логика отрабатывает, серверные операции выполняются на сервере
Ошибки «Операция недоступна» или падение при открытии формы
Один-два вызова на пользовательское действие
Серверные вызовы в цикле, заметные задержки интерфейса
Если конфигурация типовая и дорабатывается через расширение, учитывайте, что директивы компиляции методов в расширении должны соответствовать контексту расширяемого метода. Несоответствие контекстов — частая причина того, что расширение отказывается применяться или ведёт себя непредсказуемо.
При сомнениях, где выполняется код, добавьте в начало метода временную строку с #Если Сервер Тогда Сообщить("Сервер") #Иначе Сообщить("Клиент") #КонецЕсли — это быстрее, чем искать ответ в документации по конкретному событию формы.
FAQ
Чем отличается #Если Сервер от проверки ТекущийРежимЗапуска() = Неопределено?
Директива препроцессора работает на этапе компиляции и полностью исключает код из модуля в данном контексте — это единственный способ использовать типы, недоступные на другой стороне. Проверка во время выполнения лишь меняет поведение, но весь код всё равно должен компилироваться в текущем контексте.
Почему серверный метод формы не виден из клиентского кода?
Чаще всего метод объявлен без директивы &НаСервере или &НаСервереБезКонтекста, либо это метод общего модуля без признака «Вызов сервера». Проверьте директиву компиляции метода и свойства общего модуля.
Можно ли вызвать клиентский метод с сервера?
Нет. Сервер не имеет доступа к клиентскому сеансу и интерфейсу. Взаимодействие возможно только в одну сторону: клиент вызывает сервер. Для обратной связи серверный метод возвращает результат, который клиент обрабатывает после завершения вызова.
Как определить, что код выполняется в веб-клиенте?
На этапе компиляции — директивой #Если ВебКлиент Тогда. Во время выполнения в типовых конфигурациях используют служебные функции БСП, например ОбщегоНазначенияКлиент.ЭтоВебКлиент() или аналогичные, в зависимости от версии библиотеки.
Что быстрее: &НаСервере или &НаСервереБезКонтекста?
Метод без контекста выполняется быстрее, так как платформа не передаёт на сервер и обратно данные формы. Если методу не нужны реквизиты формы, всегда выбирайте вариант без контекста и передавайте нужные значения параметрами.
Почему модуль компилируется в конфигураторе, но падает в веб-клиенте?
Вероятно, проверка конфигурации выполнялась не для всех режимов. Веб-клиент имеет самый ограниченный набор доступных типов. Запустите проверку конфигурации с включённым режимом веб-клиента и скройте недоступные типы за директивами препроцессора.
Что важно запомнить
Контекст выполнения в 1С определяется на этапе компиляции: используйте #Если Сервер Тогда для исключения кода и директивы &НаКлиенте/&НаСервере в формах. ТекущийРежимЗапуска() подходит только для изменения поведения во время выполнения и на сервере возвращает Неопределено. После изменений проверяйте компиляцию во всех режимах запуска.