При интеграции 1С с веб-сайтами и внешними интерфейсами разработчики часто сталкиваются с классической архитектурной проблемой: как передать данные, введенные пользователем на сайте, в базу 1С мгновенно (в режиме реального времени) и сразу вернуть обработанный результат назад на форму. В этой статье мы подробно разберем, почему стандартные подходы с простой привязкой переменных не работают, как правильно организовать клиент-серверное взаимодействие и напишем готовое решение с использованием технологии AJAX (Fetch API) на стороне браузера и HTTP-сервиса на стороне 1С.
Рассмотрим ситуацию, в которой оказался автор темы. На веб-странице есть текстовое поле ввода (например, для ввода контрагента). Разработчик хочет, чтобы сразу после окончания ввода система отправляла текст в 1С, находила связанный договор и автоматически заполняла соседнее поле на форме. Однако данные в 1С появляются только тогда, когда пользователь переходит на другую страницу или отправляет всю форму целиком.
Выясним причину такого поведения. Это происходит из-за недопонимания принципов работы веб-технологий и клиент-серверной архитектуры:
onchange) сетевой запрос по умолчанию не отправляется, если разработчик не запрограммировал его вручную.Чтобы решить эту задачу и обеспечить мгновенный обмен данными без перезагрузки страницы, нам понадобится технология асинхронных запросов AJAX (в современном стандарте реализуемая через встроенный в браузеры метод fetch).
---
Рассмотрим подробнее, как подготовить базу 1С к приему внешних запросов. Для этого в конфигураторе нам необходимо создать новый объект метаданных — HTTP-сервис.
Выполним следующие настройки в конфигураторе:
ИнтеграцияСБраузером и зададим корневой URL: integration.ОбработкаДанных и шаблоном: /v1/process.POST (так как мы будем отправлять данные из веб-формы) и свяжем его с обработчиком в модуле HTTP-сервиса. Назовем функцию-обработчик ОбработатьДанныеPOST.Теперь напишем код обработчика в модуле HTTP-сервиса. Наша задача — принять входящий JSON-запрос от веб-страницы, извлечь из него переданную строку, выполнить необходимую бизнес-логику в 1С (например, поиск по справочникам или проведение расчетов) и вернуть структурированный JSON-ответ обратно на сайт.
Функция ОбработатьДанныеPOST(Запрос)
// Установим кодировку UTF-8 для корректного чтения кириллицы
СтрокаJSON = Запрос.ПолучитьТелоКакСтроку("UTF-8");
// Проверим, что запрос не пустой
Если ПустаяСтрока(СтрокаJSON) Тогда
Ответ = Новый HTTPСервисОтвет(400);
Ответ.УстановитьТелоИзСтроки("Пустое тело запроса", "UTF-8", ИспользованиеByteOrderMark.НеИспользовать);
Возврат Ответ;
КонецЕсли;
// Десериализуем JSON во внутреннюю структуру 1С
Чтение = Новый ЧтениеJSON;
Чтение.УстановитьСтроку(СтрокаJSON);
Попытка
ДанныеПриемника = ПрочитатьJSON(Чтение);
Чтение.Закрыть();
Исключение
Чтение.Закрыть();
Ответ = Новый HTTPСервисОтвет(400);
Ответ.УстановитьТелоИзСтроки("Некорректный формат JSON", "UTF-8", ИспользованиеByteOrderMark.НеИспользовать);
Возврат Ответ;
КонецПопытки;
// Извлекаем переданное значение из поля "inputValue"
ВходноеЗначение = "";
Если ТипЗнч(ДанныеПриемника) = Тип("Структура") И ДанныеПриемника.Свойство("inputValue") Тогда
ВходноеЗначение = СокрЛП(ДанныеПриемника.inputValue);
КонецЕсли;
// Выполняем логику на стороне 1С (например, поиск договора по названию контрагента)
// Для демонстрации просто сформируем тестовую строку ответа
ВыходноеЗначение = "";
Если ЗначениеЗаполнено(ВходноеЗначение) Тогда
ВыходноеЗначение = "Успешно обработано в 1С: Договор №" + Строка(Формат(ТекущаяДата(), "ДФ=yyMMdd")) + " для " + ВходноеЗначение;
КонецЕсли;
// Формируем структуру ответа для отправки обратно на сайт
СтруктураОтвета = Новый Структура;
СтруктураОтвета.Вставить("success", Истина);
СтруктураОтвета.Вставить("processedValue", ВыходноеЗначение);
// Сериализуем структуру ответа в JSON-строку
Запись = Новый ЗаписьJSON;
Запись.УстановитьСтроку();
ЗаписатьJSON(Запись, СтруктураОтвета);
СтрокаОтвета = Запись.Закрыть();
// Формируем HTTP-ответ
Ответ = Новый HTTPСервисОтвет(200);
Ответ.Заголовки.Вставить("Content-Type", "application/json; charset=utf-8");
// Важно: разрешаем браузеру принимать ответ (настройка CORS)
Ответ.Заголовки.Вставить("Access-Control-Allow-Origin", "*");
Ответ.УстановитьТелоИзСтроки(СтрокаОтвета, "UTF-8", ИспользованиеByteOrderMark.НеИспользовать);
Возврат Ответ;
КонецФункции
---
Проанализируем структуру нашей веб-страницы. Нам нужны два элемента управления: поле, куда пользователь вводит данные (источник), и поле, куда 1С должна динамически вернуть результат (приемник). Мы свяжем событие изменения первого поля с отправкой асинхронного фонового запроса.
Ниже представлен готовый код HTML-страницы с интегрированным скриптом на JavaScript, который использует современный стандарт Fetch API для общения с опубликованной базой 1С:
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<title>Интеграция веб-формы с 1С</title>
<style>
.form-group {
margin-bottom: 15px;
font-family: Arial, sans-serif;
}
label {
display: block;
margin-bottom: 5px;
font-weight: bold;
}
input {
width: 300px;
padding: 8px;
font-size: 14px;
}
.loading-indicator {
display: none;
color: gray;
font-size: 12px;
margin-top: 5px;
}
</style>
</head>
<body>
<div class="form-group">
<label for="Kontr_Znach">Контрагент (введите значение):</label>
<input type="text" id="Kontr_Znach" placeholder="Введите название контрагента...">
<div id="loader" class="loading-indicator">Отправка запроса в 1С...</div>
</div>
<div class="form-group">
<label for="Dog_Znach">Договор (заполняется автоматически):</label>
<input type="text" id="Dog_Znach" readonly style="background-color: #f0f0f0;">
</div>
<script>
// Находим элементы DOM на форме
const inputKontr = document.getElementById('Kontr_Znach');
const inputDog = document.getElementById('Dog_Znach');
const loader = document.getElementById('loader');
// URL публикации вашего HTTP-сервиса в 1С
// Замените "my-web-server" и "my_base" на ваши реальные данные публикации
const serviceUrl = 'http://my-web-server/my_base/hs/integration/v1/process';
// Навешиваем обработчик события "change"
inputKontr.addEventListener('change', async function() {
const val = inputKontr.value.trim();
// Если поле пустое, очищаем результирующее поле и выходим
if (!val) {
inputDog.value = '';
return;
}
// Показываем индикатор загрузки
loader.style.display = 'block';
try {
// Выполняем асинхронный POST запрос к 1С
const response = await fetch(serviceUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json; charset=utf-8'
},
body: JSON.stringify({
inputValue: val
})
});
if (response.ok) {
const resultData = await response.json();
// Если 1С вернула успешный статус, записываем данные в поле Договор
if (resultData.success) {
inputDog.value = resultData.processedValue;
} else {
console.error('Ошибка логики 1С');
inputDog.value = 'Ошибка обработки данных';
}
} else {
console.error('Сетевая ошибка при запросе к 1С. Статус:', response.status);
inputDog.value = 'Ошибка связи с сервером';
}
} catch (error) {
console.error('Не удалось выполнить запрос:', error);
inputDog.value = 'Сервер 1С недоступен';
} finally {
// Скрываем индикатор загрузки в любом случае
loader.style.display = 'none';
}
});
</script>
</body>
</html>
---
При тестировании этой схемы вы почти наверняка столкнетесь с тем, что браузер блокирует запросы к 1С, а в консоли разработчика (F12) появляется ошибка вида: Access to fetch at ... has been blocked by CORS policy. Это встроенный механизм безопасности браузеров. Если HTML-страница открыта по адресу http://mysite.com, а запрос идет на http://1c-server/base, браузер считает это угрозой безопасности (кросс-доменным запросом) и блокирует его.
Проанализируем, как правильно обойти это ограничение на стороне 1С. Перед тем как отправить основной запрос методом POST, браузер автоматически посылает предварительный проверочный запрос методом OPTIONS (так называемый preflight-запрос). Нам необходимо научить наш HTTP-сервис корректно отвечать на этот запрос.
ОбработкаДанных новый метод с именем OPTIONS и обработчиком ОбработатьДанныеOPTIONS.
Функция ОбработатьДанныеOPTIONS(Запрос)
Ответ = Новый HTTPСервисОтвет(200);
// Разрешаем запросы с любых доменов (в продакшене лучше указать конкретный домен вашего сайта)
Ответ.Заголовки.Вставить("Access-Control-Allow-Origin", "*");
// Разрешаем отправку заголовков авторизации и типов контента
Ответ.Заголовки.Вставить("Access-Control-Allow-Headers", "Content-Type, Authorization, X-Requested-With");
// Явно указываем разрешенные методы запроса
Ответ.Заголовки.Вставить("Access-Control-Allow-Methods", "POST, GET, OPTIONS");
Возврат Ответ;
КонецФункции
---
По умолчанию база 1С закрыта от неавторизованного доступа. Любой HTTP-запрос к ней потребует ввода логина и пароля пользователя 1С. Чтобы веб-сайт мог обращаться к базе в фоновом режиме без постоянного запроса пароля у конечного пользователя сайта, у нас есть два пути:
default.vrd. В нем можно прописать системного пользователя 1С, под которым будут автоматически выполняться все входящие HTTP-запросы. Для этого в файле публикации найдите блок <httpServices> и добавьте параметры авторизации:
<httpServices ibAndHttpBasicAuth="false">
<service name="ИнтеграцияСБраузером" rootUrl="integration" enable="true" reuseSessions="autouse">
<credentials user="WebUser" password="WebPassword123" />
</service>
</httpServices>
WebUser.
Authorization. Однако помните, что этот способ крайне небезопасен, так как любой пользователь сможет открыть исходный код страницы в браузере и увидеть ваши учетные данные 1С.
---
В завершение разберем важное улучшение юзабилити формы. Событие change срабатывает только тогда, когда пользователь убрал фокус ввода из поля (кликнул в другое место страницы или нажал Enter). Если мы хотим сделать интерфейс по-настоящему живым и проверять данные прямо в процессе ввода текста, нам нужно перехватывать событие input.
Но здесь кроется опасность: если отправлять сетевой запрос в 1С при вводе каждого символа, то при быстром наборе слова «Иванов» браузер сделает 6 последовательных запросов в 1С. Это создаст колоссальную и бесполезную нагрузку на сервер. Для решения этой проблемы в веб-разработке применяется паттерн Debounce (устранение дребезга). Запрос отправляется только тогда, когда пользователь сделал паузу в вводе (например, на 500 миллисекунд).
Посмотрим на пример модификации JavaScript кода с использованием Debounce:
// Функция-обертка для задержки вызова
function debounce(func, delay) {
let timeoutId;
return function (...args) {
if (timeoutId) {
clearTimeout(timeoutId);
}
timeoutId = setTimeout(() => {
func.apply(this, args);
}, delay);
};
}
// Навешиваем обработчик на событие "input" с задержкой в 600 мс
inputKontr.addEventListener('input', debounce(async function() {
const val = inputKontr.value.trim();
if (!val) {
inputDog.value = '';
return;
}
loader.style.display = 'block';
try {
const response = await fetch(serviceUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ inputValue: val })
});
if (response.ok) {
const resultData = await response.json();
if (resultData.success) {
inputDog.value = resultData.processedValue;
}
}
} catch (e) {
console.error(e);
} finally {
loader.style.display = 'none';
}
}, 600)); // Запрос уйдет в 1С только через 0.6 сек после окончания ввода
Используя данный подход, вы полностью контролируете нагрузку на вашу информационную базу 1С, обеспечивая при этом максимально отзывчивое и современное поведение веб-интерфейса для конечного пользователя.