Обмен данными — одна из самых частых задач при автоматизации бизнеса на платформе 1С:Предприятие. В современных конфигурациях на управляемых формах классические механизмы обмена (такие как тяжеловесные SOAP веб-сервисы или выгрузка в файлы) уступают место более быстрым, гибким и легковесным решениям. Использование технологии HTTP-сервисов позволяет организовать прямой бесшовный обмен между базами с минимальной нагрузкой на каналы связи и серверные мощности.
В этой статье мы подробно разберем, как правильно спроектировать, написать и внедрить обмен данными между двумя базами 1С через HTTP-сервисы. Мы рассмотрим архитектурные нюансы работы в управляемых формах, асинхронные вызовы, работу с форматом JSON, интеграцию с планами обмена, а также вопросы безопасности.
Прежде чем перейти к практике, давайте проанализируем ключевые преимущества HTTP-сервисов перед традиционными SOAP-службами:
Поскольку современный интерфейс 1С выполняется на клиенте (в тонком или веб-клиенте), выполнение длительных синхронных HTTP-запросов непосредственно из кода формы крайне нежелательно. Это приводит к блокировке (зависанию) интерфейса пользователя, что негативно сказывается на UX. Выясним, как решать эту проблему на уровне платформы.
Начиная с версии платформы 1С:Предприятие 8.3.21, разработчикам доступны встроенные асинхронные методы для работы с объектом HTTPСоединение. Эти методы работают на основе механизма обещаний (конструкции Асинх и Ждать). Они позволяют отправлять HTTP-запросы в фоновом режиме без блокировки работы пользователя.
Рассмотрим пример асинхронной отправки данных из клиентского модуля управляемой формы:
&НаКлиенте
АсинхроннаяПроцедура ВыполнитьОтправкуДанныхАсинх()
// Создаем защищенное SSL-соединение
SSLСоединение = Новый ЗащищенноеСоединениеOpenSSL();
// Настраиваем параметры подключения к базе-приемнику
Соединение = Новый HTTPСоединение("exchange.mycompany.ru", , "Администратор", "Пароль123", , 30, SSLСоединение);
Запрос = Новый HTTPЗапрос("/exchange_api/v1/put_data");
Запрос.Заголовки.Вставить("Content-Type", "application/json; charset=utf-8");
// Формируем простое тело запроса для проверки связи
СтрокаОтправки = "{""status"":""ping"", ""database"":""UT""}";
Запрос.УстановитьТелоИзСтроки(СтрокаОтправки, КодировкаТекста.UTF8, ИспользованиеБом.НеИспользовать);
Попытка
// Используем оператор Ждать для асинхронного вызова метода POST
Ответ = Ждать Соединение.ВызватьHTTPМетодАсинх("POST", Запрос);
Если Ответ.КодСостояния = 200 Тогда
ПоказатьПредупреждение(, "Данные успешно отправлены и приняты сервером!");
Иначе
ПоказатьПредупреждение(, "Ошибка сервера. Код состояния: " + Ответ.КодСостояния);
КонецЕсли;
Исключение
ПоказатьПредупреждение(, "Не удалось установить связь с сервером обмена.");
КонецПопытки;
КонецПроцедуры
Если ваша платформа ниже версии 8.3.21, или если объем передаваемых данных велик и требует длительной обработки, лучшим паттерном будет перенос отправки в фоновое задание на сервере. Клиентская форма запускает фоновое задание через ФоновыеЗадания.Выполнить(), а сама лишь периодически опрашивает его статус, выводя пользователю прогресс-бар или индикатор ожидания.
При обмене через HTTP-сервисы стандартным форматом передачи сообщений является JSON. Платформа 1С предоставляет высокопроизводительные потоковые механизмы для работы с этим форматом. Разберем по шагам, как сериализовать объект или структуру данных 1С в строку JSON.
Для быстрой сериализации массивов, структур, таблиц значений и соответствий мы используем встроенную процедуру ЗаписатьJSON(). Посмотрим на практический пример формирования пакета данных для выгрузки элемента справочника:
&НаСервереБезКонтекста
Функция СформироватьПакетДанныхНоменклатуры(СсылкаНаНоменклатуру)
// Собираем данные в структуру
ДанныеНоменклатуры = Новый Структура;
ДанныеНоменклатуры.Вставить("UUID", Строка(СсылкаНаНоменклатуру.УникальныйИдентификатор()));
ДанныеНоменклатуры.Вставить("Наименование", СсылкаНаНоменклатуру.Наименование);
ДанныеНоменклатуры.Вставить("Артикул", СсылкаНаНоменклатуру.Артикул);
ДанныеНоменклатуры.Вставить("Штрихкод", ПолучитьШтрихкодНоменклатуры(СсылкаНаНоменклатуру));
// Сериализуем структуру в строку JSON
ЗаписьJSON = Новый ЗаписьJSON;
НастройкиЗаписи = Новый ПараметрыЗаписиJSON(ПереносСтрокJSON.Нет, " ", Истина);
ЗаписьJSON.УстановитьСтроку(НастройкиЗаписи);
ЗаписатьJSON(ЗаписьJSON, ДанныеНоменклатуры);
СтрокаJSON = ЗаписьJSON.Закрыть();
Возврат СтрокаJSON;
КонецФункции
Теперь разработаем принимающую сторону. В базе-приемнике нам необходимо создать новый объект конфигурации HTTP-сервис.
Зададим следующие свойства для нашего нового сервиса:
ОбменДаннымиexchange_apiВнутри сервиса создадим шаблон URL:
PutData/v1/put_dataДля этого шаблона добавим метод POST и свяжем его с обработчиком в модуле HTTP-сервиса.
Критически важный архитектурный момент: Не пытайтесь проводить тяжелые документы и выполнять сложную бизнес-логику непосредственно внутри транзакции вызова HTTP-метода. Если база-приемник в этот момент заблокирует таблицы или обработка займет более 30 секунд, отправитель получит ошибку таймаута (например, 504 Gateway Timeout). Безопасный подход заключается в использовании очереди сообщений.
Проанализируем правильный шаблон обработчика HTTP-сервиса, который быстро сохраняет входящий пакет в очередь (регистр сведений) и возвращает статус 200 OK:
Функция v1_put_dataPOST(Запрос)
Ответ = Новый HTTPСервисОтвет(200);
Ответ.Заголовки.Вставить("Content-Type", "application/json; charset=utf-8");
// Читаем входящую строку JSON
ТелоЗапроса = Запрос.ПолучитьТелоКакСтроку(КодировкаТекста.UTF8);
Если ПустаяСтрока(ТелоЗапроса) Тогда
Ответ.КодСостояния = 400; // Bad Request
Ответ.УстановитьТелоИзСтроки("{""error"":""Тело запроса не может быть пустым""}", КодировкаТекста.UTF8);
Возврат Ответ;
КонецЕсли;
Попытка
// Быстро записываем сырые данные во входящую очередь (Регистр Сведений)
НачатьТранзакцию();
МенеджерЗаписи = РегистрыСведений.ОчередьВходящихПакетовHTTP.СоздатьМенеджерЗаписи();
МенеджерЗаписи.ИдентификаторПакета = Строка(Новый УникальныйИдентификатор);
МенеджерЗаписи.ДатаПолучения = ТекущаяДатаСеанса();
МенеджерЗаписи.ТелоПакета = ТелоЗапроса;
МенеджерЗаписи.СтатусОбработки = Перечисления.СтатусыПакетов.Новый;
МенеджерЗаписи.Записать();
ЗафиксироватьТранзакцию();
// Моментально сообщаем отправителю, что пакет принят в обработку
ОтветСтрока = "{""success"":true,""message"":""Пакет успешно зарегистрирован в очереди""}";
Ответ.УстановитьТелоИзСтроки(ОтветСтрока, КодировкаТекста.UTF8);
Исключение
ОтменитьТранзакцию();
// В случае критической внутренней ошибки сервера возвращаем 500 статус
Ответ.КодСостояния = 500;
ОшибкаОписание = КраткоеПредставлениеОшибки(ИнформацияОбОшибке());
ОтветСтрока = "{""success"":false,""error"":""" + ОшибкаОписание + """}";
Ответ.УстановитьТелоИзСтроки(ОтветСтрока, КодировкаТекста.UTF8);
ЗаписьЖурналаРегистрации("HTTP-Обмен", УровеньЖурналаРегистрации.Ошибка, , , ОшибкаОписание);
КонецПопытки;
Возврат Ответ;
КонецФункции
После записи пакета в регистр сведений, регламентное задание в базе-приемнике с заданной периодичностью (например, раз в минуту) считывает новые записи из регистра очереди, десериализует JSON и выполняет создание/проведение соответствующих документов или элементов справочников в фоновом режиме.
Для того чтобы отправлять только измененные данные и гарантировать их доставку, HTTP-сервис необходимо интегрировать со штатным механизмом платформы — Планами обмена. Это классический паттерн гарантированной доставки сообщений.
Рассмотрим сценарий работы регламентного задания выгрузки на стороне базы-источника:
200 OK.Посмотрим, как выглядит программный код выгрузки изменений с контролем доставки:
Процедура ВыполнитьВыгрузкуЗарегистрированныхИзменений() Экспорт
УзелПриемник = ПланыОбмена.ОбменЧерезHTTP.НайтиПоКоду("BASE_PR");
Если УзелПриемник.Пустая() Тогда
ВызватьИсключение "Не найден узел плана обмена для базы-приемника!";
КонецЕсли;
// Выбираем изменения порциями по 50 объектов
ВыборкаИзменений = ПланыОбмена.ВыбратьИзменения(УзелПриемник, 50);
МассивИзменений = Новый Массив;
СписокОбъектовДляУдаленияРегистрации = Новый Массив;
Пока ВыборкаИзменений.Следующий() Цикл
ДанныеОбъекта = ВыборкаИзменений.Получить();
Если ДанныеОбъекта = Неопределено Тогда
Продолжить;
КонецЕсли;
// Преобразуем объект 1С в структуру для JSON
СтруктураОбъекта = ПреобразоватьОбъектВСтруктуру(ДанныеОбъекта);
МассивИзменений.Добавить(СтруктураОбъекта);
СписокОбъектовДляУдаленияРегистрации.Добавить(ДанныеОбъекта);
КонецЦикла;
Если МассивИзменений.Количество() = 0 Тогда
Возврат; // Нечего отправлять
КонецЕсли;
// Сериализуем весь массив объектов в один JSON-пакет
ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.УстановитьСтроку();
ЗаписатьJSON(ЗаписьJSON, МассивИзменений);
СтрокаJSON = ЗаписьJSON.Закрыть();
// Отправляем на приемник
SSL = Новый ЗащищенноеСоединениеOpenSSL();
Соединение = Новый HTTPСоединение("exchange.mycompany.ru", , "ExchangeUser", "SecretPassword", , 45, SSL);
Запрос = Новый HTTPЗапрос("/exchange_api/v1/put_data");
Запрос.Заголовки.Вставить("Content-Type", "application/json");
Запрос.УстановитьТелоИзСтроки(СтрокаJSON, КодировкаТекста.UTF8, ИспользованиеБом.НеИспользовать);
Попытка
Ответ = Соединение.ОтправитьДляОбработки(Запрос);
Если Ответ.КодСостояния = 200 Тогда
// Подтверждение получено! Очищаем регистрацию изменений на источнике
ПланыОбмена.УдалитьРегистрациюИзменений(УзелПриемник, СписокОбъектовДляУдаленияРегистрации);
ЗаписьЖурналаРегистрации("HTTP-Обмен", УровеньЖурналаРегистрации.Информация, , ,
"Успешно отправлен пакет. Объектов: " + Строка(МассивИзменений.Количество()));
Иначе
ЗаписьЖурналаРегистрации("HTTP-Обмен", УровеньЖурналаРегистрации.Предупреждение, , ,
"Приемник вернул код ошибки: " + Ответ.КодСостояния);
КонецЕсли;
Исключение
ЗаписьЖурналаРегистрации("HTTP-Обмен", УровеньЖурналаРегистрации.Ошибка, , ,
"Ошибка отправки данных: " + КраткоеПредставлениеОшибки(ИнформацияОбОшибке()));
КонецПопытки;
КонецПроцедуры
Чтобы база-приемник могла принимать HTTP-запросы, ее необходимо опубликовать на веб-сервере (Apache или IIS). При публикации обязательно проверьте, что в окне настроек публикации на вкладке "HTTP сервисы" отмечен флажком ваш созданный сервис ОбменДанными.
Для обеспечения промышленного уровня безопасности соблюдайте следующие правила:
ExchangeUser) с минимально достаточными правами для записи в регистр очереди. Отключите для этого пользователя показ в списке выбора при старте программы.Мы спроектировали базовую архитектуру, гарантирующую быструю и отказоустойчивую передачу пакетов с отложенной обработкой, что минимизирует риски блокировок базы и потери данных в случае сетевых сбоев.