Флажок «Вызов сервера» у общего модуля: как клиентский код достаёт серверные процедуры

· 9 мин

Содержание

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

Типичная ситуация: разработчик пишет в форме обработчик &НаКлиенте, пытается обратиться к РаботаСТоварами.ПолучитьЦену(...) и получает ошибку компиляции. Модуль серверный, но вызов сервера для него не разрешён. Это поведение платформы 1С:Предприятие 8.3 в управляемом приложении, оно не зависит от конфигурации.

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

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

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

Что из этого следует на практике:

  • 🖥️ На клиенте нет объектов Запрос, ТаблицаЗначений, менеджеров справочников и документов. Их не получится создать в клиентском коде.
  • 🔁 Каждый вызов сервера — отдельный сетевой обмен. В тонком клиенте через интернет он стоит десятки и сотни миллисекунд.
  • 📦 Передаются только значения, которые существуют в обоих контекстах и сериализуются: примитивы, ссылки, Структура, Массив, Соответствие, их фиксированные варианты, ХранилищеЗначения.
  • 🗂️ Файловый режим не отменяет границу. Сервер работает в том же процессе, но правила передачи параметров и видимости методов те же самые.

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

Директивы компиляции в формах и свойства общего модуля

В модуле формы контекст каждой процедуры задаётся директивой компиляции:

ДирективаГде выполняетсяДоступ к данным формы
&НаКлиентеклиентда, данные формы на клиенте
&НаСервересерверда, форма передаётся на сервер и обратно
&НаСервереБезКонтекстасервернет, только параметры
&НаКлиентеНаСервереБезКонтекстатам, откуда вызвалинет

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

Вызов метода общего модуля с флажком «Вызов сервера» по поведению близок к &НаСервереБезКонтекста. Данные формы на сервер не уходят: сервер получает ровно то, что вы передали в параметрах. Это главное преимущество перед &НаСервере, где на сервер каждый раз гоняется вся форма с табличными частями.

Если серверной процедуре формы не нужны реквизиты формы, переносите её в &НаСервереБезКонтекста или в общий модуль с вызовом сервера. На формах с табличной частью на сотни строк разница заметна сразу.

Какие флажки поставить у общего модуля

Свойства открываются в Конфигураторе: выделите модуль в ветке Общие → Общие модули и нажмите Alt+Enter. Флажки контекста:

  • Клиент (управляемое приложение) — модуль компилируется в тонком, толстом и веб-клиенте управляемого приложения.
  • Клиент (обычное приложение) — для толстого клиента в обычном приложении.
  • Сервер — модуль компилируется на сервере.
  • Вызов сервера — экспортные методы серверного модуля доступны из клиентского кода.
  • Внешнее соединение — модуль доступен при подключении через COM-соединение.
  • Привилегированный — код модуля выполняется без проверки прав.
  • Глобальный — методы вызываются без указания имени модуля.

Отдельно стоит свойство «Повторное использование возвращаемых значений»: Не использовать, На время вызова, На время сеанса.

Типовые комбинации и принятые для них суффиксы имени:

Назначение модуляФлажкиСуффикс имени
Серверная логика, только для сервераСервер (+ Внешнее соединение)без суффикса
Точка входа с клиентаСервер + Вызов сервераВызовСервера
Клиентские процедурыКлиент (управляемое приложение)Клиент
Код для обоих контекстовКлиент + СерверКлиентСервер
Кеширование результатовСервер (+ Вызов сервера), повторное использованиеПовтИсп
Код без проверки правСервер + ПривилегированныйПолныеПрава

Суффиксы — соглашение из стандартов разработки, платформа их не проверяет. Но по имени ОбщегоНазначенияВызовСервера любой разработчик сразу понимает, что это серверный модуль с вызовом с клиента.

Не путайте модуль «Клиент + Сервер» с модулем «Сервер + Вызов сервера». Метод клиент-серверного модуля, вызванный с клиента, выполняется на клиенте, без обращения к серверу. Поэтому писать в нём запросы к базе нельзя.

Пошагово: делаем серверный вызов из формы

Пример: форма документа должна показать доступный остаток при выборе номенклатуры. Регистр в примере условный, подставьте свой.

Шаг 1. Создайте модуль

  1. В Конфигураторе откройте ветку Общие → Общие модули, нажмите «Добавить».
  2. Задайте имя ТоварыВызовСервера.
  3. В свойствах оставьте флажок «Сервер» и включите «Вызов сервера». Флажки клиента снимите.

Шаг 2. Напишите экспортную функцию

Функция ОстатокТовара(Номенклатура, Склад) Экспорт

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

	Выборка = Запрос.Выполнить().Выбрать();
	Если Выборка.Следующий() Тогда
		Возврат Выборка.Количество;
	КонецЕсли;

	Возврат 0;

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

Без слова Экспорт клиент функцию не увидит, даже при включённом флажке. Неэкспортные методы остаются внутренними помощниками модуля.

Шаг 3. Вызовите функцию с клиента

&НаКлиенте
Процедура ТоварыНоменклатураПриИзменении(Элемент)

	ТекущаяСтрока = Элементы.Товары.ТекущиеДанные;
	Если ТекущаяСтрока = Неопределено Тогда
		Возврат;
	КонецЕсли;

	ТекущаяСтрока.Остаток = ТоварыВызовСервера.ОстатокТовара(
		ТекущаяСтрока.Номенклатура, Объект.Склад);

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

Форма не уходит на сервер целиком: передаются две ссылки, возвращается число. Это самый дешёвый вариант серверного вызова.

Шаг 4. Проверьте результат

Запустите отладку (F5), выберите товар и убедитесь, что остаток заполнился. Чтобы увидеть, сколько времени занимает вызов, включите замер в Конфигураторе: Отладка → Замер производительности.

Ошибки при вызове сервера и как не тормозить форму

Сначала о сбоях, которые встречаются чаще всего.

Диагностика: выберите симптом

Главный враг — вызов сервера в цикле

Самая дорогая ошибка не выдаёт сообщений. Она просто превращает форму в «тормоз»:

// Плохо: 300 строк — 300 обращений к серверу
Для Каждого Строка Из Объект.Товары Цикл
	Строка.Остаток = ТоварыВызовСервера.ОстатокТовара(Строка.Номенклатура, Объект.Склад);
КонецЦикла;

При задержке 50 мс на вызов 300 строк дают 15 секунд ожидания. Правильно — собрать параметры и сходить на сервер один раз:

&НаКлиенте
Процедура ОбновитьОстатки(Команда)

	СписокТоваров = Новый Массив;
	Для Каждого Строка Из Объект.Товары Цикл
		СписокТоваров.Добавить(Строка.Номенклатура);
	КонецЦикла;

	Остатки = ТоварыВызовСервера.ОстаткиТоваров(СписокТоваров, Объект.Склад);

	Для Каждого Строка Из Объект.Товары Цикл
		Строка.Остаток = Остатки.Получить(Строка.Номенклатура);
	КонецЦикла;

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

Серверная функция ОстаткиТоваров выполняет один запрос с условием Номенклатура В (&СписокТоваров) и возвращает Соответствие. Заранее положите в него 0 для каждого товара из массива. Тогда Получить() не вернёт Неопределено для позиций без остатка.

Ещё четыре способа сократить число вызовов

  1. Объединяйте данные в один ответ. Нужны цена, остаток и единица измерения — верните Структура с тремя полями, а не три функции.
  2. Кешируйте неизменные значения. Константы, настройки пользователя, признаки функциональных опций кладите в модуль ПовтИсп с повторным использованием «На время сеанса». Для модуля с вызовом сервера кеш работает и на клиенте: повторный вызов с теми же параметрами не идёт на сервер.
  3. Не вызывайте сервер в ПриАктивизацииСтроки. Этот обработчик срабатывает при каждом перемещении курсора. Если данные нужны, подключите обработчик ожидания с задержкой 0,1–0,2 секунды.
  4. Получайте всё нужное при открытии формы. Данные, которые не меняются, удобнее заполнить в ПриСозданииНаСервере и хранить в реквизитах формы.

Не кешируйте «На время сеанса» данные, которые меняются: остатки, курсы, статусы документов. Пользователь будет видеть устаревшие значения до перезапуска сеанса.

Перед публикацией модуля с вызовом сервера
Выполнено: 0 / 7

Права доступа и безопасность серверного вызова

Экспортная функция модуля с флажком «Вызов сервера» — это публичная точка входа. Её можно вызвать не только из вашей формы. Любой клиентский код с теми же правами, в том числе код внешней обработки, подставит в неё произвольные параметры.

Правила, которые защищают от неприятностей:

  • 🔒 Проверка прав работает как обычно. Если у пользователя нет права чтения регистра, запрос внутри функции выдаст ошибку доступа. Это защита, не отключайте её без причины.
  • 🚫 Не делайте модуль с вызовом сервера привилегированным. Иначе клиент получит возможность выполнять код в обход ролей. Привилегированные операции держите в модуле ПолныеПрава, без вызова сервера.
  • 🎯 Включайте привилегированный режим точечно. Если нужна одна операция без проверки прав, оберните её в УстановитьПривилегированныйРежим(Истина) и УстановитьПривилегированныйРежим(Ложь) внутри серверного кода, после проверки параметров.
  • 🧾 Проверяйте входные данные. Тип ссылки, допустимый диапазон, принадлежность объекта текущей организации. Не принимайте с клиента текст запроса или имя таблицы: это прямой путь к выполнению чужого кода.
  • 📉 Экспортируйте минимум. Чем меньше методов открыто клиенту, тем меньше поверхность атаки и проще аудит.

Явная проверка права в начале функции выглядит так:

Функция ДанныеДляПечати(Документ) Экспорт

	ВыполнитьПроверкуПравДоступа("Чтение", Документ.Метаданные());

	// дальше — запрос и формирование результата

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

ВыполнитьПроверкуПравДоступа вызывает исключение, если права нет. Для мягкой проверки без исключения подойдёт ПравоДоступа(), она возвращает Истина или Ложь.

Транзакции, исключения и прочие тонкости

Несколько вещей, о которых обычно узнают уже после первого инцидента.

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

Исключение приходит на клиент. Если серверный код вызвал исключение, оно поднимается в клиентский код. Его можно перехватить через Попытка ... Исключение и показать пользователю понятный текст. Технические подробности при этом удобно писать в журнал регистрации на сервере.

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

Глобальный модуль с вызовом сервера — плохая идея. Методы глобального модуля доступны без имени модуля. По коду становится не видно, где идёт обращение к серверу, и вызовы расползаются по циклам.

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

Частые вопросы

Можно ли вызвать общий модуль с вызовом сервера из другого серверного кода?
Да. На сервере такой модуль ведёт себя как обычный серверный, вызов идёт без сетевого обмена.
Чем вызов общего модуля отличается от процедуры формы с директивой &НаСервере?
Процедура &НаСервере получает на сервере всю форму с реквизитами и возвращает её обратно. Общий модуль получает только параметры, поэтому вызов легче.
Почему вызов сервера разрешён только экспортным методам?
Так платформа ограничивает набор точек входа с клиента. Внутренние методы модуля остаются закрытыми и не могут быть вызваны извне.
Нужно ли перезапускать пользователей после включения флажка?
Это изменение конфигурации. Его нужно сохранить и обновить конфигурацию базы данных, а пользователи увидят изменение после перезапуска сеанса.
Можно ли передать в серверный вызов ТаблицаЗначений?
С клиента — нет, такого типа на клиенте не существует. Переложите строки в Массив из Структура и соберите таблицу на сервере.