При разработке интеграций и веб-интерфейсов на базе 1С разработчики часто сталкиваются с необходимостью передать данные из одного вызова HTTP-сервиса в другой. Например, первый запрос инициализирует и собирает сложную коллекцию (таблицу значений, массив проводок или движений документа), а второй запрос должен ее обработать. Возникает логичный вопрос: как объявить глобальную переменную для HTTP-сервиса, чтобы сохранить это промежуточное состояние?
Проанализируем ситуацию и выясним причину проблемы. Архитектура HTTP-сервисов по своей природе является "stateless" (без сохранения состояния). Каждый входящий HTTP-запрос от клиента — это, условно говоря, отдельный кратковременный запуск сеанса 1С. Как только первый запрос отработал, его контекст и все локальные переменные памяти уничтожаются. Поэтому создать классическую "глобальную переменную" между разными вызовами нельзя. Однако, рассмотрим несколько эффективных архитектурных решений этой задачи, которые мы можем применить на практике.
Разберем самый идеологически правильный для REST API способ, к которому в итоге пришел автор обсуждаемой проблемы. Если коллекция данных велика (например, коллекция проводок ДокументОбъект.Движения.Хозрасчетный) и не помещается в строку параметров URL, мы можем передавать ее в теле запроса.
Посмотрим на алгоритм совместной работы клиента и сервера 1С в этом случае:
Это снимает нагрузку с сервера 1С, не требует хранения промежуточных данных в базе и полностью соответствует стандартам построения веб-сервисов.
Если данные необходимо оставить именно на стороне сервера, мы можем временно сохранить их в базу данных: в константу, справочник или регистр сведений. Поскольку сложные коллекции нельзя напрямую записать в реквизит, используем тип ХранилищеЗначения.
Рассмотрим подробнее пример кода упаковки и распаковки данных с использованием константы:
// Упаковка (сохранение в базу при первом запросе)
КакаяТоПеременная = Новый Структура("тест1,тест2", "Строка", Новый Запрос);
УпакованнаяПеременная = Новый ХранилищеЗначения(КакаяТоПеременная);
Константы.Константа1.Установить(УпакованнаяПеременная);
// Распаковка (извлечение из базы при втором запросе)
ПеременнаяВУпаковке = Константы.Константа1.Получить();
НашаПеременная = ПеременнаяВУпаковке.Получить();
Важное замечание: у данного способа есть минус. Данные в хранилище значения невозможно обработать запросом (сгруппировать, отфильтровать) до их полной распаковки в память. Также частая запись больших объемов данных в базу может привести к снижению производительности из-за транзакций.
Еще один рабочий метод — сохранение состояния в файл на сервере. В рамках первого вызова сервиса мы записываем нашу коллекцию в файл (например, через ЗначениеВФайл или сериализацию в JSON). Затем HTTP-сервис возвращает клиенту уникальный идентификатор (имя этого временного файла).
Во время второго вызова клиент присылает этот идентификатор. 1С находит файл на диске сервера, считывает его, восстанавливает коллекцию в память и продолжает работу. После завершения обработки файл рекомендуется удалить.
Платформа 1С предоставляет удобный системный объект ХранилищеОбщихНастроек. По уникальному ключу (который можно сгенерировать в первом запросе и отдать клиенту) мы можем поместить туда нашу структуру или массив, а во втором запросе — получить эти данные. Этот метод работает на уровне базы данных, но избавляет от необходимости создавать отдельные метаданные (константы или справочники) специально для кэша HTTP-сервиса.
Начиная с платформы 8.3.9, у разработчиков появилась возможность вручную управлять жизненным циклом сеансов HTTP-сервисов. Разберем по шагам, как это работает:
IBSession: start.ПараметрыСеанса или использовать экспортные переменные модуля сеанса — они сохранят свои значения для следующего запроса.IBSession: finish, чтобы корректно освободить сеанс и вернуть его в пул платформы.При использовании механизма IBSession также становится актуальным использование функции ПоместитьВоВременноеХранилище(). Так как сеанс не уничтожается между вызовами, временное хранилище сохранит ваши данные (например, большую таблицу значений), возвратив строковый адрес, который клиент сможет использовать во втором запросе.
Проанализируем ситуацию для высоконагруженных систем. Если запросов очень много, база 1С будет тормозить от постоянной записи во временные константы, а передача мегабайтов JSON в теле запроса перегрузит сеть. В таком случае мы интегрируем 1С с In-Memory кэшем (например, Redis или Memcached).
Первый запрос сериализует коллекцию и отправляет ее в Redis (по HTTP или через компоненту), мгновенно получая токен. Токен отдается клиенту. Во втором запросе клиент присылает токен, 1С забирает данные из Redis за миллисекунды. Это корпоративный стандарт сохранения состояния для Stateless-приложений.
Подведем итог: попытка создать "глобальную переменную" в HTTP-сервисе нарушает стандарты веб-разработки. В зависимости от задачи, мы рекомендуем либо передавать данные в теле запроса (Stateless), либо использовать удержание сеанса (IBSession), либо кэшировать данные во внешних хранилищах или структурах самой платформы 1С.