При работе в веб-клиенте 1С программисты часто сталкиваются с необходимостью напрямую взаимодействовать с внешними веб-сервисами, API сторонних сайтов или локальным торговым оборудованием (например, ККТ, весы, сканеры), подключенным к компьютеру пользователя по интерфейсу RNDIS или USB. В классическом тонком клиенте эта задача легко решается стандартными серверными вызовами, но в веб-клиенте, работающем непосредственно внутри интернет-браузера, вступают в силу жесткие ограничения безопасности.
В данной статье мы подробно разберем, как обойти эти ограничения, какие современные механизмы предлагает платформа «1С:Предприятие 8.3» для отправки JSON-запросов на клиенте, и как правильно спроектировать архитектуру взаимодействия с локальными устройствами (RNDIS).
Прежде чем перейти к программным решениям, выясним причину возникновения сложностей. При исполнении кода на стороне веб-клиента мы полностью подчиняемся правилам «песочницы» браузера. Это накладывает следующие ограничения:
https://mycompany.ru/unf, а запрос отправляется на локальный адрес устройства http://192.168.137.111, браузер отклонит запрос, если удаленная сторона не вернет специальные CORS-заголовки.Рассмотрим подробнее основные способы решения этих проблем, начиная от штатных асинхронных механизмов платформы и заканчивая интеграционными прослойками.
Начиная с версии платформы 8.3.21.1302, разработчики «1С» добавили поддержку объектов HTTPСоединение и HTTPЗапрос непосредственно в веб-клиенте. Однако использовать их можно исключительно асинхронно.
Разберем по шагам, как сформировать и отправить асинхронный JSON-запрос в веб-клиенте. Начиная с версии 8.3.23, мы также можем использовать быстрые методы сериализации JSON.
Посмотрим на пример кода, который подготавливает данные и отправляет POST-запрос:
&НаКлиенте
Асинх Процедура ОтправитьДанныеНаУстройствоАсинх()
// Шаг 1. Формируем структуру данных для отправки
ПараметрыЗапроса = Новый Структура;
ПараметрыЗапроса.Вставить("Command", "PrintReceipt");
ПараметрыЗапроса.Вставить("Sum", 1500.50);
// Шаг 2. Сериализуем структуру в строку JSON (доступно в веб-клиенте с 8.3.23)
СтрокаJSON = ЗаписатьЗначениеJSON(ПараметрыЗапроса);
// Шаг 3. Создаем HTTP-запрос
Запрос = Новый HTTPЗапрос("/api/v1/execute");
Запрос.Заголовки.Вставить("Content-Type", "application/json; charset=utf-8");
Запрос.УстановитьТелоИзСтроки(СтрокаJSON, КодировкаТекста.UTF8, ИспользованиеМаркераПоследовательностиБайтов.НеИспользовать);
// Шаг 4. Инициализируем HTTP-соединение (указываем адрес устройства или сервиса)
// Обратите внимание: адрес должен поддерживать CORS заголовки!
Соединение = Новый HTTPСоединение("192.168.137.111", 8080);
Попытка
// Шаг 5. Отправляем запрос асинхронно с помощью оператора Ждать
Ответ = Ждать Соединение.ОтправитьДляОбработкиАсинх(Запрос);
Если Ответ.КодСостояния = 200 Тогда
СтрокаОтвета = Ответ.ПолучитьТелоКакСтроку(КодировкаТекста.UTF8);
// Парсим ответ обратно в структуру 1С
Результат = ПрочитатьЗначениеJSON(СтрокаОтвета);
ПоказатьПредупреждение(, "Запрос выполнен успешно! Ответ устройства: " + Результат.Status);
Иначе
ПоказатьПредупреждение(, "Ошибка сервера. Код состояния: " + Ответ.КодСостояния);
КонецЕсли;
Исключение
ИнформацияОбОшибке = ИнформацияОбОшибке();
ПоказатьПредупреждение(, "Не удалось связаться с устройством: " + ПодробноеПредставлениеОшибки(ИнформацияОбОшибке));
КонецПопытки;
КонецПроцедуры
Этот способ является наиболее лаконичным и современным, однако он требует, чтобы целевое устройство или сайт, к которому мы обращаемся, корректно обрабатывали CORS-запросы (возвращали заголовок Access-Control-Allow-Origin: *).
Если вам приходится работать на более старых версиях платформы или необходимо обойти специфические ограничения безопасности браузера, можно встроить JavaScript-код внутрь элемента формы ПолеHTMLДокумента.
Проанализируем ситуацию: мы помещаем простую HTML-страницу со скриптом AJAX во внутреннее поле 1С. Этот скрипт отправляет XMLHttpRequest или использует современный fetch API, получает JSON и передает результат обратно в 1С через генерацию события.
Разберем реализацию этого механизма:
&НаКлиенте
Процедура ИнициализироватьПолеHTML()
// Формируем HTML со встроенным JS кодом
ТекстHTML =
"<html>" +
"<head>" +
"<script type='text/javascript'>" +
"function sendJsonRequest(url, jsonData) {" +
" fetch(url, {" +
" method: 'POST'," +
" headers: {'Content-Type': 'application/json'}," +
" body: jsonData" +
" })" +
" .then(response => response.json())" +
" .then(data => {" +
" // Передаем ответ обратно в 1С через специальное событие" +
" window.parent.postMessage({type: '1C_JSON_RESPONSE', success: true, data: data}, '*');" +
" })" +
" .catch(error => {" +
" window.parent.postMessage({type: '1C_JSON_RESPONSE', success: false, error: error.message}, '*');" +
" });" +
"}" +
"</script>" +
"</head>" +
"<body>" +
"<p>Интеграционный модуль активен</p>" +
"</body>" +
"</html>";
// Записываем HTML в реквизит формы, связанный с Полем HTML
СодержимоеПоляHTML = ТекстHTML;
КонецПроцедуры
Для получения данных обратно в 1С необходимо настроить обработчик события ПриНажатии или использовать встроенный механизм сообщений (в зависимости от версии платформы и используемого браузера). Важно помнить, что современные версии платформы перешли на движки Chromium, поэтому прямая синхронная работа с DOM-деревом (свойство Документ) из кода 1С в веб-клиенте ограничена и требует асинхронного подхода.
Если устройство RNDIS подключено локально к компьютеру пользователя, обращение к нему напрямую из браузера часто натыкается на блокировки смешанного контента (HTTPS -> HTTP). Чтобы обойти это фундаментальное ограничение безопасности браузеров, используют два промышленных архитектурных решения:
localhost, в настройках которой прописаны все необходимые CORS-разрешения. Служба принимает JSON, транслирует его в устройство и возвращает ответ веб-клиенту.Для того чтобы сделать правильный выбор между предложенными вариантами, проанализируем различные эксплуатационные сценарии:
HTTPСоединение начиная с платформы 8.3.21. Так как внешние сервисы обычно корректно поддерживают работу по HTTPS и настроены на обработку запросов со стороны клиентов, вам потребуется лишь убедиться в наличии CORS-заголовков на стороне целевого API.