Как клиентский общий модуль обращается к серверу: флаг «Вызов сервера», параметры и исключения
Содержание
Чтобы вызвать серверную функцию из клиентского общего модуля 1С, нужен отдельный серверный общий модуль с флагами «Сервер» и «Вызов сервера», а сама функция в нём должна быть объявлена с ключевым словом Экспорт. Тогда в клиентском коде достаточно написать ИмяМодуля.ИмяФункции(Параметры) — платформа сама передаст параметры на сервер, выполнит функцию и вернёт результат.
Частое заблуждение — пытаться поставить директиву &НаСервере прямо в клиентском общем модуле, как в модуле формы. В общих модулях директивы компиляции не работают: где выполняется код, решают флажки в свойствах модуля. Второе заблуждение — считать, что модуль с флагами «Клиент» и «Сервер» сразу даёт серверный вызов. Не даёт: такой модуль компилируется в обоих местах, и при вызове с клиента код выполнится на клиенте.
Где проходит граница между клиентом и сервером
В управляемом приложении 1С 8.3 код живёт в двух контекстах. Клиент — это тонкий клиент, веб-клиент или мобильный клиент на компьютере пользователя. Сервер — рабочий процесс кластера 1С (в файловом варианте серверный контекст работает внутри клиентского процесса, но правила те же).
На клиенте нет доступа к базе данных. Там недоступны Запрос, объекты справочников и документов, ТаблицаЗначений. Всё, что читает или пишет данные, выполняется на сервере.
Каждое обращение клиента к серверу — это отдельный серверный вызов:
- 📦 параметры сериализуются и уходят на сервер;
- ⚙️ сервер выполняет функцию в своём контексте;
- 📨 результат (и изменённые параметры, если они переданы по ссылке) возвращается на клиент.
Важное следствие: серверный вызов не сохраняет состояние. Переменные серверного модуля между вызовами не живут, транзакция, начатая на сервере, должна закончиться в том же вызове.
В файловой базе вызов идёт без сети, поэтому «лишние» вызовы почти незаметны. Ошибки производительности всплывают только после перевода базы на клиент-сервер или публикации через веб-сервер.
Свойства общего модуля: какие флажки ставить
Откройте конфигуратор, в дереве Общие → Общие модули выделите модуль и откройте палитру свойств (Alt+Enter). Флажки определяют, где модуль компилируется и кто может к нему обращаться.
| Флажок | Что даёт | Когда ставить |
|---|---|---|
| Клиент (управляемое приложение) | Модуль доступен в тонком, веб- и мобильном клиенте | Клиентская логика: диалоги, оповещения, работа с формами |
| Сервер | Модуль компилируется на сервере | Работа с данными |
| Вызов сервера | Экспортные методы серверного модуля видны с клиента | Только для модуля-«шлюза» между клиентом и сервером |
| Внешнее соединение | Модуль доступен через COM-соединение | Интеграции через внешнее соединение |
| Привилегированный | Код выполняется без проверки прав | Осознанно, для узких задач |
| Повторное использование возвращаемых значений | Кеширует результаты функций | Редко меняющиеся данные |
Для нашей задачи нужна связка из двух модулей:
- Клиентский модуль, например
РаботаСКонтрагентамиКлиент: флажок «Клиент (управляемое приложение)», остальные сняты. - Серверный модуль
РаботаСКонтрагентамиВызовСервера: флажки «Сервер» и «Вызов сервера», «Клиент» снят.
Суффиксы Клиент, ВызовСервера, КлиентСервер и ПовтИсп взяты из стандартов разработки 1С. Платформа их не требует, но по имени модуля сразу видно его контекст, и коллеги не будут гадать, почему метод недоступен.
Не ставьте «Вызов сервера» всем серверным модулям подряд. Каждый такой модуль открывает клиенту прямой доступ к своим экспортным функциям. Держите в нём только тонкие обёртки, а основную логику — в обычных серверных модулях без этого флажка.
Синтаксис вызова и директивы компиляции
В клиентском общем модуле серверная функция вызывается как метод другого модуля — через точку:
Результат = РаботаСКонтрагентамиВызовСервера.ДанныеКонтрагента(Контрагент);
Условия, без которых вызов не скомпилируется:
- ✅ у серверного модуля стоит флажок «Вызов сервера»;
- ✅ функция объявлена с
Экспорт; - ✅ имя модуля и функции написано точно — проверка модуля (
Ctrl+F7) покажет ошибку сразу.
Директивы &НаКлиенте, &НаСервере, &НаСервереБезКонтекста используются в модулях форм и команд. В общем модуле они не нужны: контекст задают свойства. Если модуль одновременно клиентский и серверный (КлиентСервер), для разделения кода внутри него применяют инструкции препроцессора:
#Если Сервер Тогда
// этот фрагмент компилируется только на сервере
#КонецЕсли
Но и это не превращает клиентский вызов в серверный. Чтобы код реально ушёл на сервер, он должен лежать в модуле с флажком «Вызов сервера».
Почему нельзя вызвать серверный метод формы
Из клиентского общего модуля можно обратиться к экспортной клиентской процедуре формы через объект формы: Форма.ОбновитьОтображение(). А вот методы формы с &НаСервере из общего модуля недоступны. Если логика нужна и форме, и общему модулю, вынесите её в модуль ВызовСервера.
Пример: клиентский модуль получает данные контрагента с сервера
Задача: по кнопке проверить, заполнен ли ИНН у выбранного контрагента, и предупредить пользователя. Чтение данных — на сервере, сообщение — на клиенте.
Серверный модуль РаботаСКонтрагентамиВызовСервера (флажки «Сервер» и «Вызов сервера»):
Функция ДанныеКонтрагента(Знач Контрагент) Экспорт
Результат = Новый Структура("Наименование, ИНН, КПП", "", "", "");
Если Не ЗначениеЗаполнено(Контрагент) Тогда
ВызватьИсключение НСтр("ru = 'Не указан контрагент'");
КонецЕсли;
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Контрагенты.Наименование КАК Наименование,
| Контрагенты.ИНН КАК ИНН,
| Контрагенты.КПП КАК КПП
|ИЗ
| Справочник.Контрагенты КАК Контрагенты
|ГДЕ
| Контрагенты.Ссылка = &Ссылка";
Запрос.УстановитьПараметр("Ссылка", Контрагент);
Выборка = Запрос.Выполнить().Выбрать();
Если Выборка.Следующий() Тогда
ЗаполнитьЗначенияСвойств(Результат, Выборка);
КонецЕсли;
Возврат Результат;
КонецФункции
Клиентский модуль РаботаСКонтрагентамиКлиент (флажок «Клиент (управляемое приложение)»):
Процедура ПроверитьИНН(Контрагент) Экспорт
Попытка
Данные = РаботаСКонтрагентамиВызовСервера.ДанныеКонтрагента(Контрагент);
Исключение
ПоказатьПредупреждение(, КраткоеПредставлениеОшибки(ИнформацияОбОшибке()));
Возврат;
КонецПопытки;
Если ПустаяСтрока(Данные.ИНН) Тогда
ПоказатьПредупреждение(, СтрШаблон(
НСтр("ru = 'У контрагента «%1» не заполнен ИНН'"), Данные.Наименование));
КонецЕсли;
КонецПроцедуры
Из формы остаётся вызвать РаботаСКонтрагентамиКлиент.ПроверитьИНН(Объект.Контрагент) в клиентском обработчике команды.
Что здесь сделано правильно:
- 🔹 один серверный вызов возвращает сразу три поля — не по вызову на каждое;
- 🔹 на клиент уходит
Структурас примитивами, а не объект справочника; - 🔹 параметр объявлен как
Знач— ссылка не гоняется обратно на клиент; - 🔹 ошибка сервера перехвачена на клиенте и показана пользователю понятным текстом.
Передача параметров и возврат значений через границу
Между клиентом и сервером проходят только значения, которые платформа умеет сериализовать и которые существуют на клиенте.
| Можно передавать | Нельзя передавать |
|---|---|
| Строка, Число, Дата, Булево, Неопределено | СправочникОбъект, ДокументОбъект |
Ссылки: СправочникСсылка, ДокументСсылка, перечисления | ТаблицаЗначений, ДеревоЗначений |
Структура, Массив, Соответствие и их фиксированные варианты | Запрос, РезультатЗапроса, Выборка |
УникальныйИдентификатор, ДвоичныеДанные, ХранилищеЗначения | Объекты чтения и записи XML, JSON, файлов на сервере |
Правило касается и вложенных значений. Если в Структуру положить ТаблицуЗначений, вызов упадёт так же, как при передаче таблицы напрямую.
Как обойти ограничение:
- Таблицу переведите в
Массивструктур на сервере и верните массив. - Большой объём данных положите во временное хранилище (
ПоместитьВоВременноеХранилище) и передайте на клиент только адрес. - Для формы используйте реквизиты формы:
ДанныеФормыКоллекцияпередаётся в серверные методы формы штатно.
Знач и параметры по ссылке
Параметры без Знач передаются по ссылке. При серверном вызове это значит: после выполнения функции платформа отправит значение параметра обратно на клиент, даже если его никто не менял. Для крупной структуры это лишний трафик на каждом вызове.
Правило простое: если функция не должна менять входной параметр, объявляйте его как Знач. Результат возвращайте через Возврат, а не через изменение параметров.
Исключения на сервере и как их ловить на клиенте
Исключение, возникшее в серверной функции, не теряется. Оно прерывает серверный вызов и поднимается на клиенте в точке вызова. Поэтому Попытка ... Исключение вокруг вызова в клиентском модуле работает так же, как вокруг обычного кода.
Что получить в блоке Исключение:
- 🧾
ИнформацияОбОшибке()— объект с описанием, модулем и строкой ошибки; - 💬
КраткоеПредставлениеОшибки(...)— текст для пользователя; - 🛠
ПодробноеПредставлениеОшибки(...)— текст со стеком для журнала и отладки.
Порядок действий, который работает в реальных конфигурациях:
- На сервере проверяйте входные данные и вызывайте
ВызватьИсключениес понятным текстом. - Подробности ошибки пишите на сервере в журнал регистрации через
ЗаписьЖурналаРегистрации— там они полезнее, чем на экране пользователя. - На клиенте показывайте краткое представление и не продолжайте работу с пустым результатом.
Транзакцию, начатую на сервере через НачатьТранзакцию(), завершайте в том же вызове: ЗафиксироватьТранзакцию() или ОтменитьТранзакцию() в блоке Исключение. Не оставляйте открытую транзакцию в надежде закончить её следующим вызовом с клиента — так не получится, а данные останутся заблокированными до конца вызова.
Не глушите исключение пустым блоком Исключение КонецПопытки. Пользователь не увидит ошибку, а код ниже начнёт работать с Неопределено и упадёт уже в непонятном месте.
Как сократить число серверных вызовов
Скорость клиентского кода чаще всего упирается не в сам запрос, а в количество обращений к серверу. В клиент-серверной базе через интернет каждое лишнее обращение добавляет сетевую задержку.
Приёмы, которые дают эффект сразу:
- 🚫 Не вызывайте сервер в цикле. Вместо 50 вызовов по одной строке передайте массив из 50 ссылок одним вызовом и получите
Соответствиев ответ. - 📦 Собирайте данные пакетом. Одна функция возвращает
Структурусо всем, что нужно форме, а не пять функций по одному полю. - 🗂 Кешируйте постоянные данные. Для настроек и справочной информации, которая меняется редко, создайте модуль
ВызовСерверас флажком «Повторное использование возвращаемых значений» = «На время сеанса». Повторный вызов с теми же параметрами вернёт результат из кеша без обращения к серверу. - 🧹 Возвращайте минимум. Ссылка и три строки весят меньше, чем структура со всеми реквизитами объекта.
- ⏳ Долгие операции — в фоновое задание. Если расчёт идёт минуты, клиент будет ждать всё это время: серверный вызов синхронный. Запустите фоновое задание и проверяйте его состояние по обработчику ожидания.
Кешируемый модуль возвращает одно и то же значение всем вызывающим. Отдавайте из него фиксированные коллекции (ФиксированнаяСтруктура, ФиксированныйМассив), чтобы случайное изменение результата в одном месте не испортило кеш для остальных.
Когда к серверу обращаться не нужно
Часть задач решается без серверного вызова вовсе:
- 🧮 чистые вычисления — проверка контрольной суммы ИНН, форматирование суммы, разбор строки — кладите в модуль
КлиентСервер, он выполнится на месте; - 📋 данные, которые уже есть в реквизитах формы, читайте прямо на клиенте, а не перезапрашивайте из базы;
- 🔁 значения, нужные на каждом открытии формы, получайте одним вызовом при создании формы на сервере и храните в реквизите.
Как посчитать вызовы
В конфигураторе включите Сервис → Параметры → Запуск 1С:Предприятия → Дополнительно → Отображать показатели производительности и запустите отладку. В клиенте появится панель с количеством серверных вызовов и объёмом переданных данных. Нажмите кнопку на форме и посмотрите, сколько вызовов прибавилось. Если больше одного-двух на простое действие — ищите вызов в цикле.
Для детального разбора подойдёт замер производительности в отладчике: он покажет, какие строки кода сколько выполнялись.
Типовые ошибки при обращении к серверу из клиентского модуля
Экспорт или имя написано с опечаткой. Добавьте Экспорт и проверьте модуль через Ctrl+F7.ТаблицаЗначений, результат запроса. Преобразуйте данные в Структуру или Массив либо передайте адрес временного хранилища.Частые вопросы
Можно ли поставить флажки «Клиент» и «Вызов сервера» одному модулю?
Работает ли вызов сервера из глобального общего модуля?
ВызовСервера так же, как обычный клиентский. Отличие только в том, что его собственные методы доступны без имени модуля.Можно ли вызвать сервер асинхронно, не блокируя клиент?
Нужен ли флажок «Вызов сервера» для вызова из модуля формы?
&НаСервере, флажок не нужен: это уже серверный контекст. Флажок нужен, только когда к общему модулю обращаются из клиентского кода.Сбрасывается ли кеш повторного использования после изменения данных?
ОбновитьПовторноИспользуемыеЗначения().