Флаг «Вызов сервера» у общего модуля: как клиент обращается к серверному коду
Содержание
Флаг «Вызов сервера» в свойствах общего модуля делает его экспортные процедуры и функции доступными из клиентского кода. Без флага клиент их не увидит, и конфигуратор выдаст ошибку «Процедура или функция не обнаружена». Второй путь к серверу — процедура формы с директивой &НаСервере. Первый подходит для общей логики, второй для кода, привязанного к конкретной форме.
Каждое такое обращение — сетевой запрос. Один вызов стоит от единиц до сотен миллисекунд в зависимости от канала и объёма данных. Сотня вызовов в цикле превращает форму в тормоз. Ниже — как устроено взаимодействие, что передаётся, где теряется время и как не оставить дыру в правах.
Как устроено разделение клиента и сервера
В управляемом приложении код выполняется в двух местах. Клиент (тонкий, веб или мобильный) отвечает за форму и ввод. Сервер приложений держит данные, запросы, проведение документов и обращается к СУБД.
Клиент не умеет ходить в базу сам. Всё, что трогает данные, он просит у сервера. Эта граница и есть «вызов сервера».
У общего модуля несколько флагов, которые задают, где он может жить:
| Флаг | Что даёт |
|---|---|
| Сервер | Код работает на сервере. Вызывается из другого серверного кода |
| Вызов сервера | Экспортные методы доступны клиенту. Работает только вместе с флагом «Сервер» |
| Клиент (управляемое приложение) | Код выполняется на клиенте в управляемом интерфейсе |
| Клиент (обычное приложение) | То же для обычных форм |
| Внешнее соединение | Модуль доступен при подключении через COM |
| Привилегированный | Код идёт без проверки прав. Только с сервера, с клиента вызвать нельзя |
Модуль с флагом «Вызов сервера» не может одновременно быть клиентским. Платформа разведёт эти режимы: клиентский код живёт на клиенте, серверный на сервере.
Обычное имя такого модуля — с суффиксом ВызовСервера, например РаботаСКонтрагентамиВызовСервера. По названию сразу видно, что вызов дорогой.
Директивы компиляции: &НаСервере и его варианты
В модуле формы выбор места выполнения задаёт директива перед процедурой. Их четыре:
&НаКлиенте— процедура работает на клиенте.&НаСервере— на сервере, с доступом к реквизитам формы.&НаСервереБезКонтекста— на сервере, форма не передаётся.&НаКлиентеНаСервереБезКонтекста— код доступен и там, и там.
Разница между вторым и третьим пунктом решает половину проблем с производительностью.
Что такое контекст и почему он дорогой
При вызове &НаСервере платформа отправляет на сервер всю форму: значения реквизитов, таблицы, состояние элементов. Когда сервер завершил работу, изменённые данные формы возвращаются обратно.
Если в форме табличная часть на 5 000 строк, она поедет туда и обратно при каждом таком вызове, даже если вам нужно было получить одно число.
&НаСервереБезКонтекста форму не передаёт. Метод получает только параметры, которые вы явно указали. Для функций «получи значение по ссылке» это почти всегда правильный выбор.
&НаСервереБезКонтекста
Функция ПолучитьИНН(Контрагент)
Возврат Контрагент.ИНН;
КонецФункции
Привычка: сначала пишите &НаСервереБезКонтекста. Переходите на &НаСервере, только когда метод реально меняет реквизиты формы.
Когда без контекста не обойтись
Нужен доступ к Объект или к реквизитам формы — берите &НаСервере. Но передавайте работу в общий модуль, а в форме оставляйте тонкую обёртку: собрала параметры, вызвала, разобрала результат.
Что передаётся по сети и в каком виде
Параметры и результат при вызове сервера сериализуются: упаковываются в поток, уходят по сети, распаковываются. Отсюда ограничения.
✅ Передаются без проблем:
- примитивы: число, строка, дата, булево;
- ссылки на справочники и документы;
- структуры, массивы, соответствия;
- таблицы значений и списки значений;
- перечисления.
❌ Не передаются или передаются с ограничениями:
- объекты платформы, привязанные к серверу или соединению: запросы, менеджеры записи, результаты запроса;
- объекты ссылочных типов (
СправочникОбъект,ДокументОбъект) — для обмена между клиентом и сервером передают ссылку, а объект получают на сервере; - значения, которые не умеют сериализоваться, например COM-объекты.
Хорошее правило: клиент отдаёт серверу ссылки и простые данные, сервер возвращает готовый результат, а не сырые выборки.
Параметры по значению и по ссылке
В клиент-серверном вызове параметр без ключевого слова Знач считается изменяемым. Платформа вернёт его новое значение клиенту. Это лишний трафик, если вы параметр только читали.
Пишите Знач для входных параметров. Это снимает передачу значения обратно и защищает от случайной подмены.
&НаСервереБезКонтекста
Функция РассчитатьИтог(Знач Документ, Знач НаДату)
// ...
КонецФункции
Как ускорить удалённые вызовы
Узкое место — не серверный код, а число и размер обращений. Практические приёмы, от самого результативного.
Один вызов вместо цикла
Плохо: на клиенте цикл по строкам таблицы, внутри каждой итерации вызов сервера за ценой.
Хорошо: передали на сервер массив ссылок, получили соответствие «ссылка → цена» за один вызов.
Пример с цифрами. Канал даёт 50 мс на запрос. 200 строк в цикле — 10 секунд ожидания. Один вызов с массивом — те же 50 мс плюс время самого запроса.
Кэширование на клиенте
Значения, которые не меняются в течение сеанса (валюта по умолчанию, настройки пользователя), получайте один раз и храните в реквизите формы или в параметре сеанса. Для часто вызываемых серверных функций помогает свойство модуля «Повторное использование возвращаемых значений»: «На время вызова» или «На время сеанса».
Кэш «На время сеанса» отдаёт устаревшие данные, если справочник изменили в другом сеансе. Не включайте его для функций, где важна актуальность.
Пример: как переписать медленную форму
Задача: в документе реализации при выборе номенклатуры нужно показать остаток и цену. Первая версия кода на клиенте делала два вызова на каждую строку: один за остатком, второй за ценой. На документе в 40 строк это 80 обращений. При 60 мс на запрос форма «думает» почти 5 секунд.
Как исправить:
- Собрать ссылки на номенклатуру из табличной части в массив.
- Одним вызовом
&НаСервереБезКонтекстапередать массив и дату. - На сервере выполнить один запрос с соединением остатков и цен.
- Вернуть соответствие «номенклатура → структура с остатком и ценой».
- На клиенте пройти по строкам и заполнить поля без новых обращений.
Итог: 1 обращение вместо 80, ожидание около 60 мс плюс время запроса. Меняется только схема обмена, бизнес-логика остаётся прежней.
Не передавайте лишнего
Если нужна только цена, не возвращайте весь справочник. Чем меньше структура на выходе, тем быстрее она сериализуется и тем меньше трафика на мобильном канале или в веб-клиенте через VPN.
Что ещё сокращает время
| Приём | Эффект | Риск |
|---|---|---|
&НаСервереБезКонтекста вместо &НаСервере | Форма не едет по сети | Нет доступа к реквизитам |
| Один вызов с массивом | Сеть оплачивается раз | Код сложнее |
| Возврат структуры с несколькими полями | Один вызов на всё, что нужно форме | Нужно собрать данные заранее |
| Повторное использование значений | Серверный код не выполняется повторно | Устаревшие данные |
| Отложенная загрузка (по событию) | Форма открывается быстрее | Данные появляются не сразу |
Замеры лучше делать замером производительности (общий механизм «Оценка производительности»), а не на глаз. Нередко оказывается, что тормозит один запрос внутри метода, а вовсе не вызов как таковой.
Типичные ошибки при вызове сервера
Здесь собраны ситуации, на которых спотыкаются чаще всего.
Экспорт. Неэкспортные методы клиенту недоступны.&НаСервереБезКонтекста не даёт доступа к форме. Передайте нужное значение параметром либо смените директиву на &НаСервере.Знач изменить для вызывающего нельзя. Нужен возврат функцией или структурой.Отдельно про транзакции. Серверный вызов может быть частью транзакции, но граница клиент-сервер её не пересекает: вы не можете «начать транзакцию на клиенте, дописать на другом вызове». Всё, что должно записаться атомарно, выполняйте в одном серверном методе.
Права доступа и безопасность серверных вызовов
Любая экспортная функция с флагом «Вызов сервера» — публичная точка входа. Её может дёрнуть любой пользователь, у которого есть клиент, и передать любые параметры.
Что проверять в самом методе
- Права на данные. Платформа проверяет права на объекты при обращении к ним, но вы можете выполнять код в привилегированном режиме. Тогда проверку нужно делать вручную:
ПравоДоступа(...)илиРольДоступна(...). - Параметры. Клиентские данные нельзя считать доверенными. Если функция принимает ссылку на документ, убедитесь, что пользователь вообще вправе его читать.
- Результат. Не возвращайте клиенту больше, чем нужно: хранимые пароли, внутренние идентификаторы и прочие служебные поля.
Привилегированный режим
Флаг «Привилегированный» у модуля или метод УстановитьПривилегированныйРежим(Истина) снимают проверку прав. Это нужно для служебных операций: например, пользователь без прав на справочник должен получить из него одно значение.
Не оставляйте привилегированный режим включённым надолго и не передавайте через него произвольные запросы. Лучше короткий узкий метод: получил значение, вернул, режим выключился.
Привилегированный модуль нельзя вызвать с клиента напрямую. Если вам нужна такая логика, делайте обычный модуль с «Вызовом сервера», а внутри него вызывайте привилегированный.
Безопасный режим
Для кода, который приходит снаружи (внешние обработки, выражения из настроек), используйте безопасный режим: он запрещает опасные операции. Правило простое: чужой код — только в нём.
Если вызов всё равно тормозит или не работает
Когда оптимизация не помогла, двигайтесь от простого к сложному.
- Включите технологический журнал или замер производительности и найдите, где уходит время: в сети, в запросе или в коде.
- Проверьте размер передаваемых данных. Таблица значений на 100 000 строк в параметре — типичная причина «зависаний».
- Сравните скорость в тонком клиенте на сервере и по удалённому каналу. Разница покажет, виновата ли сеть.
- Если метод нужен и клиенту, и серверу, вынесите общую часть в модуль с флагами «Сервер» и «Клиент», а обращения к данным оставьте только на сервере.
- Вынесите тяжёлые операции в фоновое задание и показывайте пользователю ход выполнения, а не блокируйте форму.
Частые вопросы
Чем отличается «Вызов сервера» от просто «Сервер»?
Можно ли вызвать клиентскую процедуру из серверной?
Почему в модуле с «Вызовом сервера» нельзя ставить флаг клиента?
Что быстрее: общий модуль или процедура формы с `&НаСервере`?
&НаСервереБезКонтекста по скорости ему не уступает.