Передача данных между сервером и клиентом — одна из самых частых задач при разработке в 1С:Предприятие. Особенно актуальна эта тема, когда речь идет о работе с таблицами: таблицами значений, таблицами документов или результатами запросов. Неправильный подход может привести к ошибкам выполнения, потере данных или критическим задержкам в производительности. В этой статье мы разберем все актуальные способы передачи таблиц с сервера на клиент, их плюсы и минусы, а также типовые ошибки, которые допускают разработчики.
Основная проблема заключается в том, что серверные и клиентские контексты в 1С изолированы. Это означает, что объекты, созданные на сервере (например, результат запроса или таблица значений), нельзя напрямую использовать на клиенте. Требуются специальные механизмы для их передачи. Мы рассмотрим как стандартные методы платформы, так и оптимизированные подходы для работы с большими объемами данных.
Статья будет полезна как начинающим разработчикам, которые только осваивают клиент-серверное взаимодействие, так и опытным специалистам, ищущим способы оптимизации кода. Все примеры приведены для актуальных версий платформы 1С:Предприятие 8.3 и протестированы на реальных конфигурациях.
1. Основные способы передачи таблиц с сервера на клиент
Платформа 1С:Предприятие предоставляет несколько встроенных механизмов для передачи данных между сервером и клиентом. Выбор конкретного метода зависит от типа таблицы, объема данных и требований к производительности. Рассмотрим основные подходы:
- 🔹 Возврат значения из серверной функции — самый простой способ, подходящий для небольших таблиц
- 📊 Использование параметров процедуры/функции с модификатором
Знач - 🔄 Сериализация в JSON/XML для сложных структур данных
- 📎 Передача через временное хранилище для больших объемов данных
- 🔗 Использование общих модулей с правильно настроенными правами
Каждый из этих методов имеет свои особенности и ограничения. Например, прямая передача таблицы значений через параметры функции работает только если таблица содержит простые типы данных (число, строка, дата). Если в таблице есть сложные объекты (справочники, документы), потребуется предварительная обработка.
Рассмотрим каждый метод подробнее с практическими примерами и рекомендациями по применению.
2. Передача через возврат значения из серверной функции
Это самый простой и интуитивно понятный способ передачи данных. Серверная функция выполняет необходимые операции с таблицей и возвращает её клиенту. Основное преимущество — минимальное количество кода и высокая читаемость.
Пример кода:
&НаСервере
Функция ПолучитьТаблицуДанных()
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Номенклатура.Наименование КАК Наименование,
| Номенклатура.Артикул КАК Артикул,
| Номенклатура.Цена КАК Цена
|ИЗ
| Справочник.Номенклатура КАК Номенклатура";
РезультатЗапроса = Запрос.Выполнить();
Возврат РезультатЗапроса.Выгрузить();
КонецФункции
&НаКлиенте
Процедура КомандаПолучитьДанные(Команда)
ТаблицаДанных = ПолучитьТаблицуДанных();
// Далее работаем с таблицей на клиенте
КонецПроцедуры
Важные нюансы этого метода:
- 🔹 Работает только для таблиц с простыми типами данных
- 📌 При передаче больших таблиц (>1000 строк) может возникать задержка
- 🔄 Если нужно передать сложные объекты, их нужно преобразовать в строки или идентификаторы
⚠️ Внимание: При использовании этого метода все данные таблицы копируются в память клиента. Если таблица содержит тысячи строк с большим количеством колонок, это может привести к значительному потреблению памяти на клиентской машине.
3. Передача через параметры с модификатором Знач
Более гибкий способ — передача таблицы через параметры процедуры или функции с использованием модификатора Знач. Это позволяет явно указать платформе, что параметр нужно передать по значению, а не по ссылке.
Пример реализации:
&НаСервере
Процедура ЗаполнитьТаблицуНаКлиенте(ТаблицаНаКлиенте) Экспорт
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ ПЕРВЫЕ 100 * ИЗ Справочник.Контрагенты";
Результат = Запрос.Выполнить();
ТаблицаНаСервере = Результат.Выгрузить();
// Передаем таблицу по значению
ТаблицаНаКлиенте(Знач ТаблицаНаСервере);
КонецПроцедуры
&НаКлиенте
Процедура ПолучитьДанныеКонтрагентов(Команда)
ТаблицаКонтрагентов = Новый ТаблицаЗначений;
ЗаполнитьТаблицуНаКлиенте(ТаблицаКонтрагентов);
// Теперь ТаблицаКонтрагентов заполнена данными с сервера
КонецПроцедуры
Преимущества этого метода:
- 🔹 Более явный контроль над передачей данных
- 📊 Возможность передачи не только таблиц значений, но и других объектов
- 🔄 Лучшая производительность при передаче средних объемов данных (100-1000 строк)
⚠️ Внимание: При передаче таблиц со ссылочными типами (справочники, документы) через параметры Знач происходит автоматическое преобразование ссылок в строки. Это может привести к потере функциональности, если на клиенте требуется работать именно со ссылками.
4. Сериализация таблиц в JSON/XML
Для передачи сложных структур данных или когда требуется сохранение состояния объектов между сеансами, удобно использовать сериализацию в форматы JSON или XML. Этот метод особенно полезен при интеграции с внешними системами.
Пример сериализации таблицы в JSON:
&НаСервере
Функция ПолучитьДанныеВJSON()
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ ПЕРВЫЕ 50 Наименование, Артикул ИЗ Справочник.Номенклатура";
Результат = Запрос.Выполнить();
Таблица = Результат.Выгрузить();
ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.УстановитьСтроку();
ЗаписьJSON.ЗаписатьНачалоОбъекта();
ЗаписьJSON.ЗаписатьКлюч("Данные");
ЗаписьJSON.ЗаписатьНачалоМассива();
Для Каждого Строка Из Таблица Цикл
ЗаписьJSON.ЗаписатьНачалоОбъекта();
ЗаписьJSON.ЗаписатьКлюч("Наименование");
ЗаписьJSON.ЗаписатьЗначение(Строка.Наименование);
ЗаписьJSON.ЗаписатьКлюч("Артикул");
ЗаписьJSON.ЗаписатьЗначение(Строка.Артикул);
ЗаписьJSON.ЗаписатьКонецОбъекта();
КонецЦикла;
ЗаписьJSON.ЗаписатьКонецМассива();
ЗаписьJSON.ЗаписатьКонецОбъекта();
Возврат ЗаписьJSON.Закрыть();
КонецФункции
На клиенте полученный JSON можно десериализовать:
&НаКлиенте
Процедура ОбработатьJSON(Команда)
JSONСтрока = ПолучитьДанныеВJSON();
ЧтениеJSON = Новый ЧтениеJSON;
ЧтениеJSON.УстановитьСтроку(JSONСтрока);
ТаблицаРезультат = Новый ТаблицаЗначений;
ТаблицаРезультат.Колонки.Добавить("Наименование");
ТаблицаРезультат.Колонки.Добавить("Артикул");
ЧтениеJSON.Прочитать();
Данные = ЧтениеJSON.ПрочитатьJSON();
Для Каждого Элемент Из Данные.Данные Цикл
НоваяСтрока = ТаблицаРезультат.Добавить();
НоваяСтрока.Наименование = Элемент.Наименование;
НоваяСтрока.Артикул = Элемент.Артикул;
КонецЦикла;
КонецПроцедуры
Преимущества сериализации:
- 🔹 Возможность передачи сложных вложенных структур
- 📊 Совместимость с внешними системами
- 🔄 Легкость хранения и логирования передаваемых данных
⚠️ Внимание: Сериализация/десериализация больших таблиц (>1000 строк) может значительно нагружать процессор как на сервере, так и на клиенте. Для оптимизации рекомендуется использовать потоковую обработку JSON.
☑️ Подготовка к сериализации таблицы
5. Использование временного хранилища
Для передачи больших объемов данных (тысячи строк) наиболее эффективным решением является использование временного хранилища. Этот механизм позволяет разделить процесс передачи на два этапа: запись данных на сервере и последующее чтение на клиенте.
Пример работы с временным хранилищем:
&НаСервере
Функция ПодготовитьДанныеВоВременномХранилище()
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ * ИЗ Документ.РеализацияТоваровУслуг";
Результат = Запрос.Выполнить();
ТаблицаДокументов = Результат.Выгрузить();
// Создаем временное хранилище
Хранилище = Новый ХранилищеЗначения(ТаблицаДокументов, 3600); // Хранится 1 час
Возврат Хранилище.ПолучитьИдентификатор();
КонецФункции
&НаКлиенте
Процедура ПолучитьДанныеИзХранилища(Команда)
Идентификатор = ПодготовитьДанныеВоВременномХранилище();
// Получаем данные из хранилища
Хранилище = Новый ХранилищеЗначения(Идентификатор);
ТаблицаДокументов = Хранилище.Получить();
// Работаем с полученной таблицей
КонецПроцедуры
Особенности использования временного хранилища:
| Параметр | Описание | Рекомендуемое значение |
|---|---|---|
| Время жизни | Период хранения данных в секундах | 3600 (1 час) для больших отчетов |
| Макс. размер | Ограничение на объем данных | До 100 Мб в стандартной поставке |
| Тип данных | Какие объекты можно хранить | Любые сериализуемые объекты |
| Безопасность | Доступ к данным хранилища | Только в рамках текущего сеанса |
Преимущества временного хранилища:
- 🔹 Оптимально для передачи очень больших объемов данных
- 📊 Разделение нагрузки: сервер записывает, клиент читает в удобное время
- 🔄 Возможность кэширования часто используемых данных
⚠️ Внимание: Временное хранилище не предназначено для долговременного хранения данных. После истечения времени жизни или завершения сеанса данные будут автоматически удалены. Не используйте его как замену базе данных.
6. Оптимизация производительности при передаче таблиц
Независимо от выбранного метода передачи, при работе с большими объемами данных важно учитывать вопросы производительности. Вот ключевые рекомендации по оптимизации:
1. Минимизация объема передаваемых данных:
- 🔹 Передавайте только необходимые колонки (используйте
ВЫБРАТЬс явным перечислением полей) - 📊 Применяйте фильтрацию на сервере (WHERE в запросах)
- 🔄 Используйте постраничную загрузку для больших таблиц
2. Оптимизация структуры данных:
- 🔹 Преобразуйте сложные объекты в простые типы перед передачей
- 📊 Для больших таблиц рассмотрите возможность передачи только идентификаторов с последующей подгрузкой деталей
3. Технические приемы:
- 🔹 Используйте
ПоместитьВоВременноеХранилищедля данных >1000 строк - 📊 Для JSON-сериализации больших объектов используйте потоковую запись
- 🔄 Рассмотрите возможность асинхронной загрузки данных на клиенте
Пример оптимизированного запроса:
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 1000
| Номенклатура.Ссылка КАК Ссылка,
| Номенклатура.Наименование КАК Наименование,
| Номенклатура.Артикул КАК Артикул
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| Номенклатура.ПометкаУдаления = ЛОЖЬ
| И Номенклатура.ЭтоГруппа = ЛОЖЬ
|УПОРЯДОЧИТЬ ПО
| Наименование";
Для таблиц более 5000 строк рассмотрите возможность передачи данных порциями по 1000 строк с использованием механизма "показать еще" на клиентской форме.
7. Типовые ошибки и их решение
При передаче таблиц с сервера на клиент разработчики часто сталкиваются с типовыми ошибками. Рассмотрим наиболее распространенные проблемы и способы их решения:
| Ошибка | Причина | Решение |
|---|---|---|
| Нельзя использовать значение типа "СправочникСсылка.Номенклатура" в клиентском контексте | Попытка передать ссылку на объект без преобразования | Преобразуйте ссылку в строку (УникальныйИдентификатор() или ПредставлениеСсылки()) |
| Превышен максимальный размер передаваемых данных | Передача слишком большой таблицы | Используйте временное хранилище или постраничную загрузку |
| Неверный формат JSON | Ошибка при сериализации/десериализации | Проверьте структуру данных и используйте отладчик JSON |
| Данные во временном хранилище истекли | Слишком маленькое время жизни | Увеличьте время хранения или реализуйте повторную загрузку |
Рассмотрим подробнее решение самой частой проблемы — работы со ссылочными типами:
Как правильно передавать ссылки на объекты?
При передаче ссылок на справочники или документы через границу сервер-клиент происходит автоматическое преобразование в строковое представление. Чтобы избежать потери функциональности:
1. На сервере преобразуйте ссылку в уникальный идентификатор:
Идентификатор = Объект.УникальныйИдентификатор();
2. Передайте идентификатор клиенту (через параметры или JSON)
3. На клиенте при необходимости восстановите объект:
Объект = Справочники.Номенклатура.ПолучитьСсылку(Новый УникальныйИдентификатор(Идентификатор));
Это позволит избежать ошибок контекста и сохранит возможность работы с объектами на клиенте.
Еще одна распространенная проблема — потеря данных при передаче больших таблиц. Это обычно происходит из-за ограничений на размер передаваемых данных между сервером и клиентом. Решения:
- 🔹 Разбейте данные на порции по 500-1000 строк
- 📊 Используйте временное хранилище для данных >1000 строк
- 🔄 Реализуйте прогрессивную загрузку с индикатором процесса
8. Практический пример: передача таблицы документов
Рассмотрим полный пример передачи таблицы документов "РеализацияТоваровУслуг" с сервера на клиент с учетом всех оптимизаций и обработки возможных ошибок.
Серверная часть:
&НаСервере
Функция ПолучитьСписокРеализаций(ДатаНачала, ДатаОкончания, ТолькоПроводенные = Истина) Экспорт
Попытка
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Реализация.Ссылка КАК Ссылка,
| Реализация.Номер КАК Номер,
| Реализация.Дата КАК Дата,
| Реализация.Контрагент КАК Контрагент,
| Реализация.СуммаДокумента КАК Сумма
|ИЗ
| Документ.РеализацияТоваровУслуг КАК Реализация
|ГДЕ
| Реализация.Дата МЕЖДУ &ДатаНачала И &ДатаОкончания
| " + (?(ТолькоПроводенные, "И Реализация.Проводен = ИСТИНА", "")) + "
|УПОРЯДОЧИТЬ ПО
| Дата УБЫВ";
Запрос.УстановитьПараметр("ДатаНачала", ДатаНачала);
Запрос.УстановитьПараметр("ДатаОкончания", ДатаОкончания);
Результат = Запрос.Выполнить();
Таблица = Результат.Выгрузить();
// Преобразуем ссылки в идентификаторы для безопасной передачи
Для Каждого Строка Из Таблица Цикл
Если ТипЗнч(Строка.Ссылка) = Тип("СправочникСсылка.Контрагенты") Тогда
Строка.КонтрагентИд = Строка.Контрагент.УникальныйИдентификатор();
Строка.Контрагент = Строка.Контрагент.Наименование;
КонецЕсли;
Строка.Ссылка = Строка.Ссылка.УникальныйИдентификатор();
КонецЦикла;
Возврат Таблица;
Исключение
ЗаписьЖурналаРегистрации(НСтр("ru = 'Ошибка при получении реализаций: '") + ОписаниеОшибки(),
УровеньЖурналаРегистрации.Ошибка,
,
,
,
КаталогИБ() + "\Logs\Integration.log");
Возврат Новый ТаблицаЗначений; // Возвращаем пустую таблицу при ошибке
КонецПопытки;
КонецФункции
Клиентская часть:
&НаКлиенте
Процедура ЗагрузитьРеализации(Команда)
ДатаНачала = НачалоДня(ТекущаяДата());
ДатаОкончания = КонецДня(ТекущаяДата());
Попытка
ТаблицаРеализаций = ПолучитьСписокРеализаций(ДатаНачала, ДатаОкончания);
Если ТаблицаРеализаций.Количество() = 0 Тогда
ПоказатьПредупреждение(НСтр("ru = 'Нет данных за указанный период'"));
Возврат;
КонецЕсли;
// Восстанавливаем ссылки из идентификаторов
Для Каждого Строка Из ТаблицаРеализаций Цикл
Попытка
Строка.Ссылка = Документы.РеализацияТоваровУслуг.ПолучитьСсылку(
Новый УникальныйИдентификатор(Строка.Ссылка));
Строка.Контрагент = Справочники.Контрагенты.ПолучитьСсылку(
Новый УникальныйИдентификатор(Строка.КонтрагентИд));
Исключение
// Если не удалось восстановить ссылку, оставляем как есть
ЗаписьВЛог(НСтр("ru = 'Ошибка восстановления ссылки: '") + ОписаниеОшибки());
КонецПопытки;
КонецЦикла;
// Отображаем результат пользователю
ОткрытьФорму("СписокРеализаций", Новый Структура("Таблица", ТаблицаРеализаций));
Исключение
ПоказатьОшибку(ОписаниеОшибки());
КонецПопытки;
КонецПроцедуры
Этот пример демонстрирует:
- 🔹 Безопасную передачу ссылочных типов через уникальные идентификаторы
- 📊 Обработку ошибок на обоих уровнях (сервер и клиент)
- 🔄 Восстановление объектов на клиенте по идентификаторам
- 📎 Логирование ошибок для последующего анализа
При передаче таблиц документов всегда преобразуйте ссылки в уникальные идентификаторы на сервере и восстанавливайте объекты на клиенте. Это единственный надежный способ избежать ошибок контекста.
Часто задаваемые вопросы
Можно ли передавать таблицу значений с вложенными таблицами?
Да, можно, но требуется особая обработка. Вложенные таблицы нужно сериализовать в JSON или XML на сервере, а на клиенте десериализовать обратно. Платформа 1С не поддерживает автоматическую передачу вложенных таблиц через стандартные механизмы.
Пример:
// На сервере
ВложеннаяТаблицаJSON = Новый ЗаписьJSON;
ВложеннаяТаблицаJSON.УстановитьСтроку();
ВложеннаяТаблицаJSON.Записать(ВложеннаяТаблица);
ОсновнаяТаблица.Колонки.Добавить("ВложеннаяТаблицаJSON");
НоваяСтрока.ВложеннаяТаблицаJSON = ВложеннаяТаблицаJSON.Закрыть();
Как передать таблицу с более чем 10 000 строк?
Для таких больших объемов данных рекомендуется:
- Использовать временное хранилище с увеличенным временем жизни
- Реализовать постраничную загрузку (по 1000-2000 строк)
- Рассмотреть возможность выгрузки данных в файл (CSV, Excel) с последующей загрузкой на клиенте
Пример постраничной загрузки:
// Серверная функция с параметрами страницы
Функция ПолучитьСтраницуДанных(НомерСтраницы, РазмерСтраницы)
Смещение = (НомерСтраницы - 1) * РазмерСтраницы;
Запрос.Текст = "ВЫБРАТЬ ... СМЕЩЕНИЕ &Смещение ЛИМИТ &РазмерСтраницы";
// ... остальной код
Почему при передаче таблицы теряются некоторые колонки?
Это может происходить по нескольким причинам:
- Колонки содержат несериализуемые данные (например, двоичные данные)
- Имена колонок содержат специальные символы
- При передаче через JSON/XML не все колонки включены в сериализацию
Решение: Проверьте типы данных всех колонок и при необходимости преобразуйте их в строки или числа перед передачей.
Как передать таблицу с клиента на сервер?
Механизмы передачи работают симметрично. Можно использовать:
- Передачу через параметры серверной процедуры с модификатором
Знач - Сериализацию в JSON/XML
- Временное хранилище (клиент записывает, сервер читает)
Пример:
&НаКлиенте
Процедура ОтправитьДанныеНаСервер(Команда)
Таблица = ПолучитьТаблицуСФормы();
Результат = СервернаяПроцедура(Знач Таблица);
КонецПроцедуры
&НаСервере
Функция СервернаяПроцедура(Таблица)
// Обработка данных
КонецФункции
Какие ограничения на размер передаваемых данных?
Ограничения зависят от метода передачи:
| Метод | Ограничение | Рекомендации |
|---|---|---|
| Прямая передача | ~1-5 Мб (зависит от настроек сервера) | Для данных <1000 строк |
| Временное хранилище | До 100 Мб в стандартной поставке | Для данных >1000 строк |
| JSON/XML | Ограничивается памятью | Для структурированных данных |
Для передачи очень больших объемов (>100 Мб) рассмотрите возможность выгрузки в файл на сервере и последующей загрузки его на клиенте.