Как передать значение из клиентского модуля в серверный без изменения параметров процедуры в 1С?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

В процессе разработки на платформе 1С часто возникает задача передать некоторые данные с клиента на сервер. Стандартный способ — добавить новый параметр в вызываемую серверную процедуру. Но что делать, если эта процедура находится очень глубоко в цепочке вызовов, и изменение ее сигнатуры повлечет за собой рефакторинг множества других процедур и функций? Это трудоемко и рискованно. Рассмотрим ситуацию, когда нужно в одном клиентском модуле сохранить значение в некий буфер, а затем прочитать его в другом серверном модуле в рамках текущего сеанса. При этом изменять параметры процедур, создавать новые константы или параметры сеанса — нежелательно.

Проанализируем несколько рабочих и надежных способов организации такого "скрытого" канала передачи данных.

Способ 1: Хранилище общих настроек

Это механизм платформы, предназначенный для хранения пользовательских настроек между сеансами. Однако его можно успешно применять и для временной передачи данных внутри одного сеанса. Основная особенность, которую нужно учесть: объект ХранилищеОбщихНастроек недоступен напрямую в коде тонкого клиента.

Решение — инкапсулировать работу с хранилищем в процедуры общего серверного модуля. Мы создаем две простые экспортные процедуры: одну для записи значения, другую для чтения.

Посмотрим на пример. Создадим общий серверный модуль, например, УправлениеБуферомОбмена.


// Общий серверный модуль "УправлениеБуферомОбмена"

Процедура СохранитьЗначениеВБуфер(Ключ, Значение) Экспорт
	
	ХранилищеОбщихНастроек.Сохранить("МойБуферДанных", Ключ, Значение);
	
КонецПроцедуры

Функция ЗагрузитьЗначениеИзБуфера(Ключ) Экспорт
	
	Возврат ХранилищеОбщихНастроек.Загрузить("МойБуферДанных", Ключ);
	
КонецФункции

Процедура ОчиститьЗначениеВБуфере(Ключ) Экспорт

    ХранилищеОбщихНастроек.Удалить("МойБуферДанных", Ключ);

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

Теперь использовать этот механизм очень просто. Сначала в клиентской процедуре вызываем серверную обертку для сохранения данных:


&НаКлиенте
Процедура ПередВызовомСервернойЛогики()
    
    // Сохраняем некое важное значение перед вызовом сложной логики
    ДанныеДляПередачи = "Это важная строка, которую нужно прочитать на сервере";
    УправлениеБуферомОбмена.СохранитьЗначениеВБуфер("КлючМоихДанных", ДанныеДляПередачи);
    
    // Теперь вызываем серверную процедуру, в которую не хотим добавлять параметр
    ВыполнитьСложнуюСервернуюЛогику();
    
КонецПроцедуры

А внутри глубоко вложенной серверной процедуры мы можем прочитать это значение:


&НаСервере
Процедура КакаяТоВложеннаяПроцедура()
    
    // Здесь нам понадобились данные, сохраненные на клиенте
    ПолученныеДанные = УправлениеБуферомОбмена.ЗагрузитьЗначениеИзБуфера("КлючМоихДанных");
    
    Если ПолученныеДанные <> Неопределено Тогда
        // Используем полученные данные
        Сообщить("На сервере успешно получено значение: " + ПолученныеДанные);
    КонецЕсли;

    // Важно! Не забываем очищать буфер после использования, чтобы не оставлять "мусор"
    УправлениеБуферомОбмена.ОчиститьЗначениеВБуфере("КлючМоихДанных");
    
КонецПроцедуры

Преимущества: Простота реализации, не требует изменения структуры метаданных.

Недостатки: Небольшие накладные расходы на сериализацию/десериализацию. Необходимо тщательно следить за уникальностью ключей и очищать данные после использования.

Способ 2: Регистр сведений

Это, пожалуй, самый распространенный и надежный "промышленный" способ для решения подобных задач. Создается специальный технический регистр сведений, который выступает в роли буфера.

Разберем по шагам:

  1. Создаем новый Регистр сведений в конфигураторе, назовем его, например, ВременныеДанныеСеанса.
  2. Режим записи регистра — Независимый.
  3. В измерениях (закладка "Данные") создадим поля для идентификации наших данных:
    • Пользователь (тип СправочникСсылка.Пользователи) — для привязки данных к конкретному пользователю.
    • КлючДанных (тип Строка) — уникальный идентификатор наших данных, аналог ключа из предыдущего примера. Можно использовать УникальныйИдентификатор, если требуется гарантированная уникальность.
  4. В ресурсах создадим поле для хранения самого значения. Чтобы хранить данные любого типа, используем тип ХранилищеЗначения. Назовем ресурс Значение.

Теперь работа с этим регистром выглядит так. Сначала пишем данные:


&НаСервере
Процедура ЗаписатьДанныеВРегистрНаСервере(Ключ, Данные)

    // Получаем текущего пользователя
    ТекущийПользователь = Пользователи.ТекущийПользователь();

    // Создаем менеджер записи
    МенеджерЗаписи = РегистрыСведений.ВременныеДанныеСеанса.СоздатьМенеджерЗаписи();
    МенеджерЗаписи.Пользователь = ТекущийПользователь;
    МенеджерЗаписи.КлючДанных = Ключ;
    МенеджерЗаписи.Значение = Новый ХранилищеЗначения(Данные);
    
    // Записываем данные в базу
    МенеджерЗаписи.Записать();

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

А затем читаем в другом месте:


&НаСервере
Функция ПрочитатьДанныеИзРегистраНаСервере(Ключ)

    ТекущийПользователь = Пользователи.ТекущийПользователь();
    
    // Читаем данные из регистра
    СтруктураОтбора = Новый Структура("Пользователь, КлючДанных", ТекущийПользователь, Ключ);
    Запись = РегистрыСведений.ВременныеДанныеСеанса.Получить(СтруктураОтбора);
    
    Если Запись <> Неопределено И Запись.Свойство("Значение") Тогда
        // Очищаем запись, так как она больше не нужна
        НаборЗаписей = РегистрыСведений.ВременныеДанныеСеанса.СоздатьНаборЗаписей();
        НаборЗаписей.Отбор.Пользователь.Установить(ТекущийПользователь);
        НаборЗаписей.Отбор.КлючДанных.Установить(Ключ);
        НаборЗаписей.Записать(); // Записываем пустой набор для удаления

        Возврат Запись.Значение.Получить();
    Иначе
        Возврат Неопределено;
    КонецЕсли;

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

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

Преимущества: Надежность, прозрачность (данные видны в базе), возможность хранить любые типы данных.

Недостатки: Требует изменения конфигурации, создает дополнительную нагрузку на базу данных, требует механизма очистки.

Способ 3: Временное хранилище

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

Проанализируем схему работы:

  1. В серверной процедуре мы помещаем наши данные (например, таблицу значений, структуру или двоичные данные) во временное хранилище с помощью функции ПоместитьВоВременноеХранилище().
  2. Эта функция возвращает уникальный адрес — строку вида "e1cib/tempstorage/...".
  3. Этот строковый адрес мы можем легко передать обратно на клиент и сохранить, например, в реквизите формы. Он не несет никакой секретной информации.
  4. При следующем вызове сервера мы передаем этот адрес в другую серверную процедуру, которая с помощью функции ПолучитьИзВременногоХранилища() извлекает по нему исходные данные.

Посмотрим на пример, где мы сначала сохраняем данные, а потом в другом вызове их читаем.


&НаСервере
Функция СохранитьДанныеИПолучитьАдрес()

    СтруктураДанных = Новый Структура;
    СтруктураДанных.Вставить("Параметр1", "Значение1");
    СтруктураДанных.Вставить("Параметр2", 123);
    
    Адрес = ПоместитьВоВременноеХранилище(СтруктураДанных, УникальныйИдентификатор);
    Возврат Адрес;

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

&НаСервере
Процедура ИспользоватьДанныеПоАдресу(Адрес)

    Данные = ПолучитьИзВременногоХранилища(Адрес);
    
    Если Данные <> Неопределено Тогда
        // Данные - это наша исходная структура
        Сообщить(Данные.Параметр1); // "Значение1"
    КонецЕсли;

    // Хранилище очищается платформой автоматически после завершения запроса,
    // в котором был последний раз получен объект из хранилища.
    // Но можно и удалить принудительно:
    УдалитьИзВременногоХранилища(Адрес);

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

Преимущества: Идеально подходит для больших объемов данных (файлы, таблицы), управляется платформой, не требует изменения метаданных.

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

Способ 4: Кэширующие модули (с повторным использованием возвращаемых значений)

Это очень быстрый и элегантный способ для обмена данными в пределах одного сеанса. Он не затрагивает базу данных вообще.

Как это работает:

  1. Создаем общий серверный модуль, например, СеансовыйКэш.
  2. В его свойствах устанавливаем флаг "Повторное использование возвращаемых значений". Это ключевой шаг!
  3. Внутри модуля объявляем экспортную переменную на уровне модуля через ключевое слово Перем.

Благодаря установленному флагу, состояние этой переменной будет сохраняться между разными серверными вызовами в рамках одного сеанса. Это, по сути, создает "глобальную" переменную на время жизни сеанса на сервере.

Пример реализации модуля СеансовыйКэш:


// Общий серверный модуль "СеансовыйКэш"
// В свойствах модуля ОБЯЗАТЕЛЬНО установить флаг
// "Повторное использование возвращаемых значений"

Перем КонтейнерЗначений Экспорт; // Наша "глобальная" переменная

Процедура ИнициализироватьКэш() Экспорт
    
    Если КонтейнерЗначений = Неопределено Тогда
        КонтейнерЗначений = Новый Соответствие;
    КонецЕсли;
    
КонецПроцедуры

Процедура УстановитьЗначение(Ключ, Значение) Экспорт
    
    ИнициализироватьКэш();
    КонтейнерЗначений.Вставить(Ключ, Значение);
    
КонецПроцедуры

Функция ПолучитьЗначение(Ключ) Экспорт
    
    ИнициализироватьКэш();
    Возврат КонтейнерЗначений[Ключ];
    
КонецФункции

Использование ничем не отличается от других методов: в одном серверном вызове пишем, в другом — читаем.

Преимущества: Максимальная производительность, так как все операции происходят в оперативной памяти сервера без обращений к СУБД. Не требует изменения метаданных.

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

Вывод: выбор конкретного способа зависит от задачи. Для быстрой передачи простых данных внутри сеанса идеально подойдет кэширующий модуль. Для передачи больших объемов данных — временное хранилище. Если нужна повышенная надежность или данные должны "пережить" короткие разрывы связи, лучше использовать регистр сведений. А ХранилищеОбщихНастроек — это хороший компромисс между простотой и функциональностью.

← На главную