Разработка и Конфигурация · 11 мин
1С: как передать результат запроса с серверной процедуры на клиент
Разбираем способы передачи результата запроса с сервера на клиент в 1С: массивы структур, таблицы значений, хранилище значений. Примеры кода и типичные ошибки.
В управляемых формах 1С код разделён на клиентский и серверный контексты. Запросы к базе данных выполняются только на сервере, а отобразить данные нужно на клиенте — в таблице формы, списке выбора или диалоге. Проблема в том, что объект РезультатЗапроса и выборка из него нельзя напрямую вернуть на клиент: эти типы не сериализуются, и платформа выдаст ошибку о недопустимом типе при вызове серверной процедуры.
Поэтому задача сводится к преобразованию результата запроса в тип, допустимый для передачи между контекстами. К таким типам относятся примитивные значения, структуры, массивы, таблицы значений (в ограниченных сценариях), а также сериализуемые форматы — строка JSON или значение в хранилище значения. Выбор способа зависит от объёма данных, необходимости типизации и дальнейшего использования на клиенте.
Ниже разобраны рабочие варианты с примерами кода, ограничения каждого подхода и типичные ошибки, из-за которых передача ломается или работает медленно.
Почему результат запроса нельзя вернуть напрямую
Объекты РезультатЗапроса и ВыборкаИзРезультатаЗапроса существуют только в серверном контексте и содержат внутренние ссылки на соединение с базой данных. При возврате из серверной процедуры платформа пытается сериализовать значение, и для этих типов сериализация невозможна — возникает ошибка вида «Ошибка передачи данных между клиентом и сервером» или «Недопустимый тип значения».
То же ограничение касается менеджеров объектов, построителей запросов и других прикладных объектов, привязанных к контексту сервера. Правило простое: между клиентом и сервером могут перемещаться только сериализуемые типы — примитивы, структуры, массивы, соответствия, таблицы и деревья значений, ссылки на объекты базы данных и значения в хранилище значения.
Способ 1. Массив структур — универсальный вариант
Самый распространённый приём: на сервере обойти выборку запроса и собрать массив структур, где каждая структура описывает одну строку. Такой массив свободно возвращается на клиент и там перебирается в цикле.
&НаСервере
Функция ПолучитьДанныеНаСервере()
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ ПЕРВЫЕ 100 Номенклатура.Ссылка КАК Ссылка,
|Номенклатура.Наименование КАК Наименование
|ИЗ Справочник.Номенклатура КАК Номенклатура";
Результат = Запрос.Выполнить();
Выборка = Результат.Выбрать();
МассивДанных = Новый Массив;
Пока Выборка.Следующий() Цикл
Строка = Новый Структура("Ссылка, Наименование");
ЗаполнитьЗначенияСвойств(Строка, Выборка);
МассивДанных.Добавить(Строка);
КонецЦикла;
Возврат МассивДанных;
КонецФункции
&НаКлиенте
Процедура Загрузить(Команда)
Данные = ПолучитьДанныеНаСервере();
Для Каждого Строка Из Данные Цикл
Сообщить(Строка.Наименование);
КонецЦикла;
КонецПроцедуры
Метод ЗаполнитьЗначенияСвойств копирует поля выборки в структуру, если имена свойств совпадают с именами колонок запроса. Это избавляет от ручного перечисления полей, но требует следить за соответствием имён.
Плюсы подхода: простой код, полная типизация значений, отсутствие лишних зависимостей. Минус — при большом числе строк массив структур занимает заметно больше памяти, чем компактная таблица значений, и передача по сети становится тяжёлой.
Способ 2. Таблица значений и реквизит формы
Если данные нужно показать в табличной части формы, лучше не передавать их как возвращаемое значение, а заполнить реквизит формы типа «ТаблицаЗначений» прямо на сервере. Форма передаётся между контекстами целиком, поэтому отдельная сериализация таблицы не требуется.
&НаСервере
Процедура ЗаполнитьТаблицуНаСервере()
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ Товар, Количество ИЗ РегистрНакопления.Остатки.Остатки";
ТаблицаРезультат = Запрос.Выполнить().Выгрузить();
Товары.Загрузить(ТаблицаРезультат);
КонецПроцедуры
Здесь Товары — реквизит формы с типом «ТаблицаЗначений» и колонками, совпадающими по именам с колонками результата. Метод Загрузить копирует данные построчно. После возврата управления на клиент таблица уже отображается в интерфейсе.
Возвращать «чистую» таблицу значений из серверной функции тоже можно — платформа её сериализует. Однако на практике чаще удобнее реквизит формы: данные сразу привязаны к элементу отображения и не требуют ручного переноса.
Способ 3. JSON для обмена и больших данных
Когда данные нужно передать не только на клиент формы, но и во внешнюю систему, или когда структура строк заранее неизвестна, результат удобно сериализовать в JSON. На сервере формируется массив структур или соответствий, затем он преобразуется в строку через ЗаписьJSON.
&НаСервере
Функция ДанныеВJSONНаСервере()
Запрос = Новый Запрос("ВЫБРАТЬ Ссылка, Код ИЗ Справочник.Контрагенты");
Выборка = Запрос.Выполнить().Выбрать();
Массив = Новый Массив;
Пока Выборка.Следующий() Цикл
Массив.Добавить(Новый Структура("Код", Выборка.Код));
КонецЦикла;
Запись = Новый ЗаписьJSON;
Запись.УстановитьСтроку();
ЗаписатьJSON(Запись, Массив);
Возврат Запись.Закрыть();
КонецФункции
На клиенте строка разбирается через ПрочитатьJSON. Учитывайте особенность: ссылки в JSON сериализуются неочевидным образом, поэтому для обмена лучше передавать строковое представление уникального идентификатора или код элемента, а ссылку восстанавливать на сервере при необходимости.
Способ 4. Хранилище значения и временное хранилище
Для действительно больших выборок передача массива структур по сети нежелательна. Альтернатива — поместить результат во временное хранилище формы и вернуть на клиент только адрес. Клиент при необходимости запрашивает данные по этому адресу или передаёт адрес в другую серверную процедуру.
&НаСервере
Функция ПоместитьВХранилищеНаСервере()
Таблица = Запрос.Выполнить().Выгрузить();
Возврат ПоместитьВоВременноеХранилище(Таблица, УникальныйИдентификатор);
КонецФункции
Передача УникальныйИдентификатор формы вторым параметром привязывает данные к времени жизни формы: после её закрытия хранилище очищается автоматически. Без этого параметра данные живут до конца сеанса и создают утечку памяти на сервере.
Сравнение способов и критерии выбора
| Способ | Когда подходит | Ограничения |
|---|---|---|
| Массив структур | Небольшие и средние выборки, обработка на клиенте | Расход памяти растёт с числом строк |
| Реквизит формы (таблица значений) | Отображение в табличном поле формы | Требует описания колонок в реквизите |
| JSON-строка | Обмен с внешними системами, динамическая структура | Потеря типизации ссылок, лишнее преобразование |
| Временное хранилище | Большие выборки, передача между вызовами | Нужно управлять временем жизни данных |
Сравнение вариантов
Универсально и просто; подходит до нескольких тысяч строк, дальше начинает тормозить передача по сети.
Лучший вариант для отображения в таблице формы; не подходит, если клиенту нужна только часть данных.
Незаменим для интеграций; избыточен для внутренней передачи внутри формы.
Единственный разумный вариант для очень больших результатов; требует дисциплины очистки.
Проверка результата и типичные ошибки
После реализации передачи проверьте три вещи: данные дошли без потерь, типы значений сохранились, а время вызова приемлемо на реальном объёме. Быстрый способ — вывести количество полученных строк на клиенте и сравнить с числом записей в выборке на сервере.
Как проверить результат
Совпадает с числом строк выборки на сервере
Строк меньше или массив пуст — проверяйте условия запроса и заполнение структуры
Даты остаются датами, числа — числами, ссылки доступны
Всё превратилось в строки — признак лишней сериализации в JSON
Вызов занимает секунды на типовом объёме
Десятки секунд — сокращайте выборку или переходите на хранилище
Отдельно отметим директивы компиляции. Код с запросом должен находиться в методе с директивой &НаСервере в модуле формы либо в серверном общем модуле с флагом «Сервер». Вызов из клиентского метода выполняется просто по имени — платформа сама организует серверный вызов, а возвращаемое значение сериализует.
Что важно запомнить
Результат запроса и выборку на клиент вернуть нельзя — преобразуйте их в сериализуемый тип. Для формы используйте реквизит-таблицу, для обработки на клиенте — массив структур, для интеграций — JSON, для больших объёмов — временное хранилище. Всегда ограничивайте выборку и проверяйте типы после передачи.
Частые вопросы
Можно ли вернуть с сервера сам объект Запрос или РезультатЗапроса?
Нет. Эти объекты привязаны к серверному контексту и не сериализуются. Попытка вернуть их из серверной функции приведёт к ошибке передачи данных. Передавайте результат преобразования — таблицу значений, массив структур или строку JSON.
Что быстрее: массив структур или таблица значений?
При передаче между клиентом и сервером таблица значений обычно компактнее по памяти на больших объёмах, но массив структур удобнее в обходе на клиенте и не требует описания колонок. Для нескольких сотен строк разница незаметна — выбирайте по удобству дальнейшей обработки.
Как передать результат запроса в другую форму?
Удобнее всего поместить таблицу значений во временное хранилище в первой форме и передать адрес через параметры открываемой формы. Во второй форме данные извлекаются по адресу методом ПолучитьИзВременногоХранилища на сервере. Так не придётся гонять весь массив данных через параметры.
Почему после передачи ссылки превратились в строки?
Такое происходит при промежуточной сериализации в JSON или XML, где ссылка сохраняется как строковое представление. Если ссылки нужны на клиенте как ссылки, используйте массив структур или таблицу значений — эти типы передают ссылки корректно. Из JSON ссылку придётся восстанавливать на сервере по уникальному идентификатору.
Работает ли передача в тонком и веб-клиенте?
Да, механизм сериализации одинаков для толстого, тонкого и веб-клиента. Различия касаются только доступности отдельных типов в клиентском контексте: например, в тонком и веб-клиенте недоступны некоторые объекты работы с данными, но массивы, структуры и таблицы значений работают везде.
Как ограничить объём передаваемых данных?
Используйте конструкцию ВЫБРАТЬ ПЕРВЫЕ в тексте запроса, накладывайте отборы через параметры, выбирайте только необходимые поля вместо звёздочки. Для произвольного просмотра больших списков реализуйте постраничную загрузку: сервер возвращает очередную порцию по смещению, а клиент запрашивает следующую по мере прокрутки.