1С JAVASCRIPT. 1С NODE JS

Всем привет, с вами Низамов Илья! 1С позволяет нам расширять возможности платформы с помощью внешних компонент. Данная технология позволяет нам делать кроссплатформенные компоненты, которые будут работать на любых операционных системах.

Помимо внешних компонент, возможности 1С можно расширять и другими способами, в том числе и с помощью JavaScript. В данной статье рассмотрим интеграцию 1С JAVASCRIPT как на клиенте, так и на сервере.

JavaScript в 1С: поле HTML документа

Поле HTML документа — это встроенный в форму 1С браузерный движок. Через него можно в обе стороны: вызвать JS-функцию из кода 1С и получить результат, либо наоборот — передать данные из JS обратно в 1С. Ниже — минимальный пример без привязки к конкретной задаче; развёрнутый рабочий кейс (расчёт HMAC для Amazon S3) — дальше по статье.

Реквизит формы — тип Строка, вид поля в конструкторе формы — «Поле HTML документа». В макет типа «Текстовый документ» кладём HTML со скриптом:

<html>
<body>
<script>
function multiply(a, b) {
  return a * b;
}
</script>
</body>
</html>

Загружаем HTML в поле при создании формы и вызываем функцию из клиентского кода 1С через defaultView:

&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
	Элементы.ПолеHTML.Документ.write(ПолучитьТекстИзМакета("html_js"));
КонецПроцедуры

&НаКлиенте
Функция УмножитьЧерезJS(а, б)
	ОкноДокумента = Элементы.ПолеHTML.Документ.defaultView;
	Если ОкноДокумента = Неопределено Тогда
		Возврат Неопределено;
	КонецЕсли;
	Возврат ОкноДокумента.multiply(а, б);
КонецФункции

Направление «1С вызывает JS» работает предсказуемо начиная с платформы 8.3.5. Обратное направление — JS передаёт данные в 1С — устроено сложнее: отдельного API для этого нет, на практике используют эмуляцию клика по невидимому элементу формы. В JS создают событие и «стреляют» им в DOM, а 1С ловит его в обработчике поля:

// внутри страницы, загруженной в поле HTML документа
var evt = document.createEventObject();
evt.propertyName = "СобытиеИзJS";
evt.data = JSON.stringify({value: 42});
document.body.fireEvent("onclick", evt);
&НаКлиенте
Процедура ПолеHTMLПриОбработкеРезультата(Элемент, ЕщеРезультат, ДопПараметры)
	Если ДопПараметры.Свойство("Event") И ДопПараметры.Event.propertyName = "СобытиеИзJS" Тогда
		Данные = ПрочитатьJSON(ДопПараметры.Event.data);
		// дальнейшая обработка на стороне 1С
	КонецЕсли;
КонецПроцедуры

Технику document.createEventObject/fireEvent используют потому, что поле HTML документа в толстом и тонком клиенте 1С под Windows исторически работает на движке Internet Explorer. В веб-клиенте и на других платформах движок может отличаться — перед тем как закладывать это в продакшн, проверьте поведение на своей версии платформы и на том клиенте, где реально будет открываться форма.

Возможности JS я описывать не буду. Это достаточно мощный язык с развитым комьюнити и множеством готовых библиотек, уроков по нему предостаточно на Youtube.

В данном примере я буду его использовать для расчета hash с использованием hmac sha256. В интернете я нашел много примеров на 1С, но все они неправильно рассчитывали hash для доступа к REST API Amazon S3. Поэтому я стал использовать COM компоненту «System.Text.UTF8Encoding» и «System.Security.Cryptography.HMACSHA256»

Для начала я рекомендую посмотреть вам мои уроки, в которых я описываю процесс доступа к REST API Amazon.

Amazon S3 + 1C. Часть 1. Интерфейс

Amazon S3 + 1C. Часть 2. Получение списка файлов

Amazon S3 + 1C. Часть 3. Загрузка файла с облака

Amazon S3 + 1С. Часть 4. Выгрузка файлов в облако

В уроках получилась вот такая функция:

Функция HMAC_SHA256(InKey, String) 

Текст = Новый COMОбъект("System.Text.UTF8Encoding"); 
Криптография = Новый COMОбъект("System.Security.Cryptography.HMACSHA256"); 

Если ТипЗнч(InKey) = Тип("Строка") Тогда 
SecKey = Текст.GetBytes_4(InKey); 
Иначе 
SecKey = InKey; 
КонецЕсли; 

Криптография.Key = SecKey; 
Возврат Криптография.ComputeHash_2(Текст.GetBytes_4(String)); 
КонецФункции

Но проблема данной функции в том, что она работает только на Windows. Для кроссплатформенности я решил использовать JavaScript.

1С JAVASCRIPT НА КЛИЕНТЕ

Для расчета хешей на JavaScript есть отличная библиотека Crypto-js. Вот ее и будем использовать. Саму функцию я разработал и протестировал в IDE WebStorm, она очень простая.

function getSignatureKey(JSONData) { 
var data1s = JSON.parse(JSONData); 

var kDate = CryptoJS.HmacSHA256(data1s.dateStamp, "AWS4" + data1s.key); 
var kRegion = CryptoJS.HmacSHA256(data1s.regionName, kDate); 
var kService = CryptoJS.HmacSHA256(data1s.serviceName, kRegion); 
var kSigning = CryptoJS.HmacSHA256("aws4_request", kService); 
var kSignature = CryptoJS.HmacSHA256(data1s.stringToSign, kSigning); 
return JSON.stringify({"hash": kSignature.toString(CryptoJS.enc.Hex)}); 
}

В функцию передаем параметры для расчета и внутри нее вызываем необходимую функцию из библиотеки. Обратно отдаем результат в формате JSON.

Теперь эту функцию надо как-то вызвать из 1С. Для этого создаем реквизит формы с типом Строка и размещаем его на форме. Вид у поля указываем «Поле HTML документа».

Саму функцию JS я разместил в макете обработки. Тип макета «Текстовый документ» и скопировал туда функцию JS.

Как видите, в обработке присутствуют еще несколько макетов. Они содержат зависимые библиотеки на JavaScript. Все эти библиотеки так же скопированы в макеты типа «Текстовый документ»

Для доступа к функции JS, надо сформировать простой html документ. Делаю я это при создании формы.

Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка) 

hmac = ПолучитьТекстИзМакета("hmac"); 
cryptojs = ПолучитьТекстИзМакета("cryptojs"); 
encbase64 = ПолучитьТекстИзМакета("encbase64"); 
hmacsha256 = ПолучитьТекстИзМакета("hmacsha256"); 

ШаблонHTML = " 




| HMAC 
| %1 
| %2 
| %3 
| %4 



|"; 

ПолеHTML = СтрШаблон(ШаблонHTML, cryptojs, hmacsha256, encbase64, hmac); 

КонецПроцедуры

Сначала получаем код скриптов, а потом подставляем его в наш шаблон между тегами <script></script>

Теперь мы можем вызвать нашу функцию JS, но только из клиентских функций.

&НаКлиенте 
Функция HMAC_JS(Данные, StringToSign) 

ОкноДокумента = ЭтаФорма.Элементы.ПолеHTML.Документ.defaultView; 
Если ОкноДокумента = Неопределено Тогда 
Возврат ""; 
КонецЕсли; 

ДанныеСтруктура = Новый Структура; 
ДанныеСтруктура.Вставить("key", Данные["SecretAccessKey"]); 
ДанныеСтруктура.Вставить("dateStamp", Данные["date"]); 
ДанныеСтруктура.Вставить("regionName", Данные["Region"]); 
ДанныеСтруктура.Вставить("serviceName", "s3"); 
ДанныеСтруктура.Вставить("stringToSign", StringToSign); 

ДанныеJSON = СформироватьJSON(ДанныеСтруктура); 

Результат = ПрочитатьДанныеИзJSON(ОкноДокумента.getSignatureKey(ДанныеJSON)); 

Возврат Результат["hash"]; 
КонецФункции

JAVASCRIPT НА СЕРВЕРЕ

Но некоторым возможно понадобится использовать данный расчет на сервере. К сожалению, JS можно без костылей использовать только на клиенте. Для использования на сервере у вас должен стоять Node.js.

Node.js — это JavaScript-окружение построенное на движке Chrome V8, спроектирован для построения масштабируемых сетевых приложений. Это просто офигенно быстрый сервер, который работает на всех платформах. Реализацию простейшего серверного скрипта вы можете посмотреть по ссылке. Запускать эти скрипты вы можете в командной строке.

Ниже приведен код моего скрипта. Данный скрипт запускает локальный web сервер на 3000 порту. Принимает GET запросы для проверки соединения и POST запросы для обработки данных.

function amazonSig(JSONData) { 
MyObject = { 
res: null, 
err: null 
}; 

try { 
var CryptoJS = require("crypto-js"); 

var data1s = JSON.parse(JSONData); 

var kDate = CryptoJS.HmacSHA256(data1s.dateStamp, "AWS4" + data1s.key); 
var kRegion = CryptoJS.HmacSHA256(data1s.regionName, kDate); 
var kService = CryptoJS.HmacSHA256(data1s.serviceName, kRegion); 
var kSigning = CryptoJS.HmacSHA256("aws4_request", kService); 
var kSignature = CryptoJS.HmacSHA256(data1s.stringToSign, kSigning); 

MyObject.res = kSignature.toString(CryptoJS.enc.Hex); 

} catch (e) { 
MyObject.err = e.toString(); 


return MyObject; 


var http = require("http"); 

http.createServer(function(request, response){ 
const { headers, method, url } = request; 
let body = []; 

// Устанавливаем заголовки 
response.setHeader('Content-Type', 'application/json'); 

request.on('error', (err) => { 
console.error(err); 
response.statusCode = 404; 
}).on('data', (chunk) => { 
// Получаем тело запроса 
body.push(chunk); 
}).on('end', () => { 
// Преобразуем тело запроса в строку 
body = Buffer.concat(body).toString(); 

if (method == "POST") { 
// Расчитываем hash и возвращаем результат 
var res = amazonSig(body); 
if ( res.err != null) { 
response.statusCode = 404; 
} else { 
response.end(JSON.stringify(res)); 

} else { 
response.end("OK"); 

}); 

}).listen(3000);

В 1С скрипт хранится макете обработки в виде zip архива. Он распаковывается во временную папку при запуске формы и из 1С выполняется команда для его запуска.

&НаСервереБезКонтекста 
Функция ЗапуститьСерверNodeJSНаСервере() 

Результат = ВыполнитьЗапросКСерверуNodeJS("GET"); 
Если Результат = Ложь ИЛИ Результат = Неопределено Тогда 
Каталог = КаталогВременныхФайлов() + "nodesrv\"; 
ИмяАрхива = Каталог + "srv.zip"; 

ДД = Обработки.AmazonS3.ПолучитьМакет("nodejs"); 
ДД.Записать(ИмяАрхива); 

Архив = Новый ЧтениеZipФайла(ИмяАрхива); 
Архив.ИзвлечьВсе(Каталог); 

ЗапуститьПриложение("node " + Каталог + "cp.js"); 
Результат = ВыполнитьЗапросКСерверуNodeJS("GET"); 
Если Результат = Неопределено Тогда 
Возврат Ложь; 
КонецЕсли; 
КонецЕсли; 

Возврат Истина; 
КонецФункции

Потом идет отправка GET запроса к нему и если все ОК, то наш мини сервис готов к обработке данных.

&НаСервереБезКонтекста 
Функция HMAC_JSСервер(Данные, StringToSign) 

Каталог = КаталогВременныхФайлов(); 

ДанныеСтруктура = Новый Структура; 
ДанныеСтруктура.Вставить("key", Данные["SecretAccessKey"]); 
ДанныеСтруктура.Вставить("dateStamp", Данные["date"]); 
ДанныеСтруктура.Вставить("regionName", Данные["Region"]); 
ДанныеСтруктура.Вставить("serviceName", "s3"); 
ДанныеСтруктура.Вставить("stringToSign", StringToSign); 

ДанныеJSON = СформироватьJSONСервер(ДанныеСтруктура); 

Ответ = ВыполнитьЗапросКСерверуNodeJS("POST", ДанныеJSON); 
Если НЕ Ответ = Неопределено Тогда 
ДанныеJS = ПрочитатьДанныеИзJSONНаСервере(Ответ); 
Возврат ДанныеJS["res"]; 
КонецЕсли; 

Возврат Неопределено; 
КонецФункции &НаСервереБезКонтекста 
Функция ВыполнитьЗапросКСерверуNodeJS(ВидЗапроса, Данные = Неопределено) 

Попытка 
HTTPСоединение = Новый HTTPСоединение("localhost", 3000,,,, 2); 
HTTPЗапрос = Новый HTTPЗапрос("/"); 
Если НЕ Данные = Неопределено Тогда 
HTTPЗапрос.УстановитьТелоИзСтроки(Данные); 
КонецЕсли; 
Ответ = HTTPСоединение.ВызватьHTTPМетод(ВидЗапроса, HTTPЗапрос); 
Если Ответ.КодСостояния = 200 Тогда 
Если ВидЗапроса = "POST" Тогда 
Возврат Ответ.ПолучитьТелоКакСтроку(); 
Иначе 
Возврат Истина; 
КонецЕсли; 
Иначе 
Возврат Неопределено; 
КонецЕсли; 
Исключение 
Возврат Неопределено; 
КонецПопытки; 

Возврат Неопределено; 
КонецФункции

Теперь обработка отправки и получения файлов с облака Amazon работает на всех системах. Правильнее конечно написать на C++ внешнюю Native компоненту, но это уже будет в другой статье.

Разработать Native внешнюю компоненты вы сможете ознакомившись с циклом статей. Начните с этой.

Скачать архив скрипта JS

Поле HTML документа или отдельный микросервис: что выбрать

Это два разных по своей природе способа добавить JS в контур 1С, и выбор между ними — не про «что лучше», а про то, где физически должен выполняться код.

КритерийJS в поле HTML документаОтдельный микросервис (Node.js / Python)
Где выполняется кодВнутри клиента 1С, на машине пользователяНа отдельном сервере или в контейнере, доступном по сети
ИнфраструктураНе нужна — весь JS зашит в макеты конфигурацииНужен рантайм, деплой, мониторинг, обновления
Подходит дляРазовых вычислений на клиенте — хеширование, форматирование, готовые JS-библиотеки без серверной частиДолгих операций, внешних интеграций, LLM-запросов — всего, что не должно тормозить сеанс 1С
Зависимость от клиента 1СПоведение отличается между толстым, тонким и веб-клиентомНе зависит от клиента — обычный HTTP-запрос
МасштабированиеОграничено машиной пользователяМожно поднять несколько инстансов, добавить очередь

Разовый расчёт на стороне клиента без сети — повод оставить всё в поле HTML документа: не нужна инфраструктура, работает офлайн. Как только задача — обращение к внешнему API, очереди или тяжёлые вычисления, вынос логики в отдельный микросервис становится оправданным: подробный разбор подхода на Python и FastAPI, включая сравнение с Node.js/Express, — в статье «Python + 1С: микросервис на FastAPI для интеграции с 1С».

Если нужен фундамент по HTTP-обмену со стороны 1С — разбираем HTTP-сервисы и REST на курсе по интеграции 1С через HTTP и REST API.

Похожие статьи

Если фронтенд для 1С — тоже ваша задача, на курсе по фронтенду на Nuxt.js для 1С разбираем, как собрать веб-интерфейс поверх 1С без поля HTML документа и его ограничений.