Свои веб-страницы на 1С 8.3: от публикации базы до приёма POST-запросов

· 10 мин

Содержание

Чтобы сделать страницы в 1С, которые открываются в обычном браузере, нужны три вещи: HTTP-сервис в конфигурации, публикация базы на веб-сервере (Apache или IIS) и функция-обработчик, которая возвращает HTTPСервисОтвет с HTML в теле. Адрес такой страницы выглядит как http://сервер/имя_публикации/hs/корневой_URL/шаблон.

Если же нужен готовый интерфейс 1С в браузере, без своей вёрстки, хватит публикации веб-клиента. А если под «страницами» вы понимаете вкладки на форме документа или обработки, это группа «Страницы» в редакторе формы, о ней в отдельном разделе ближе к концу.

Какой способ сделать страницы выбрать

У платформы 8.3 несколько механизмов, которые отдают что-то наружу через веб. Для своих страниц подходят не все.

СпособЧто получает пользовательКогда выбираютОграничения
Веб-клиентФормы 1С в браузере, как в тонком клиентеУдалённая работа сотрудников в базеСвоей вёрстки нет, нужна лицензия на каждое подключение
HTTP-сервисЛюбой HTML, JSON, файл, который вы сформировали кодомЛичный кабинет, витрина, анкета, отчёт по ссылкеВсю вёрстку и логику пишете сами
Web-сервис (SOAP)XML по WSDL-описаниюОбмен с другими системамиНе для браузера
ODataДанные объектов по стандартному протоколуВыгрузка в BI и внешние приложенияСтраниц не формирует
Поле HTML-документа на формеHTML внутри формы 1СКрасивый отчёт или справка прямо в базеВидно только из клиента 1С

Дальше речь про HTTP-сервисы: только они дают полностью свою страницу по своему адресу.

💡 Веб-клиент и HTTP-сервисы публикуются одной и той же процедурой. Можно включить оба варианта в одной публикации или только сервисы.

Публикация базы на веб-сервере для HTTP-сервисов

Без публикации HTTP-сервис существует только в конфигурации, браузер его не увидит.

Что установить заранее

  • 🧩 Компонент платформы «Модули расширения веб-сервера». Его выбирают в установщике платформы. Без него пункт публикации есть, но веб-сервер не сможет загрузить модуль 1С.
  • 🌐 Веб-сервер: IIS (входит в Windows Server) или Apache 2.4. Разрядность Apache и платформы должна совпадать: 64-битная платформа работает с 64-битным Apache.
  • 🔑 Права: учётная запись, от имени которой работает веб-сервер, должна читать каталог bin платформы, а для файловой базы ещё и писать в каталог базы.

Порядок публикации

  1. Запустите Конфигуратор от имени администратора Windows. Без этого платформа не сможет изменить настройки IIS или файл httpd.conf.
  2. Откройте Администрирование → Публикация на веб-сервере.
  3. Задайте имя публикации латиницей, например shop. Оно станет частью адреса.
  4. Выберите веб-сервер и каталог публикации. В каталог запишется файл default.vrd.
  5. На закладке HTTP-сервисов включите публикацию сервисов по умолчанию или отметьте нужные сервисы списком.
  6. Нажмите «Опубликовать» и согласитесь на перезапуск веб-сервера.

Перезапуск Apache или IIS обрывает все веб-сеансы, которые идут через этот сервер: веб-клиент, обмены через HTTP, мобильные приложения. На рабочем сервере публикуйте вне рабочих часов.

Файл default.vrd

Публикация — это по сути файл default.vrd плюс строки в конфигурации веб-сервера. Файл можно править вручную, например чтобы задать пользователя для анонимного доступа:

<?xml version="1.0" encoding="UTF-8"?>
<point xmlns="http://v8.1c.ru/8.2/virtual-resource-system"
       xmlns:xs="http://www.w3.org/2001/XMLSchema"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       base="/shop"
       ib="File=&quot;C:\Bases\Shop&quot;;Usr=web;Pwd=СложныйПароль;">
  <httpServices publishByDefault="true"/>
</point>

Если Usr и Pwd в строке подключения нет, браузер запросит логин и пароль пользователя 1С (Basic-аутентификация). Для публичных страниц удобнее отдельный пользователь web с одной узкой ролью: чтение нужных справочников и запись только в регистр обращений.

Никогда не прописывайте в default.vrd пользователя с ролью «Полные права» — любой, кто найдёт адрес, получит доступ ко всему, что умеют ваши сервисы.

Создаём HTTP-сервис и обработчик GET-запроса

Структура в дереве конфигурации

HTTP-сервисы лежат в ветке Общие → HTTP-сервисы. Внутри сервиса три уровня:

  • 📦 Сервис со свойством «Корневой URL», например site.
  • 🧭 Шаблоны URL: /, /items, /items/{code}. Фигурные скобки задают параметр.
  • ⚙️ Методы каждого шаблона: GET, POST, PUT, DELETE или * (любой метод). У метода есть свойство «Обработчик», двойной клик по нему создаёт функцию в модуле сервиса.

Итоговый адрес шаблона /items/{code} при публикации shop:

http://localhost/shop/hs/site/items/00-00000012

Часть /hs/ добавляет платформа, её не нужно указывать в корневом URL.

Первая страница

Обработчик получает объект HTTPСервисЗапрос и обязан вернуть HTTPСервисОтвет:

Функция ГлавнаяGET(Запрос)

    Html = "<!DOCTYPE html><html lang=""ru""><head><meta charset=""utf-8"">"
        + "<title>Каталог</title></head><body>"
        + "<h1>Каталог товаров</h1><p><a href=""items"">Открыть список</a></p>"
        + "</body></html>";

    Ответ = Новый HTTPСервисОтвет(200);
    Ответ.Заголовки.Вставить("Content-Type", "text/html; charset=utf-8");
    Ответ.УстановитьТелоИзСтроки(Html, КодировкаТекста.UTF8, ИспользованиеByteOrderMark.НеИспользовать);
    Возврат Ответ;

КонецФункции

Без заголовка Content-Type браузер может показать исходный текст вместо страницы или «кракозябры». Метку BOM лучше отключать: на некоторых браузерах и прокси она даёт лишний символ в начале документа.

После изменения конфигурации обновите конфигурацию базы данных. Повторно публиковать базу не нужно, если в публикации включены сервисы по умолчанию.

Параметры адреса и строки запроса

  • Запрос.ПараметрыURL["code"] — значение из шаблона /items/{code}.
  • Запрос.ПараметрыЗапроса.Получить("q") — параметр после знака вопроса: /items?q=кабель. Если параметра нет, вернётся Неопределено.
  • Запрос.HTTPМетод и Запрос.Заголовки пригодятся, когда один обработчик обслуживает метод *.

Генерация HTML-страницы из данных базы

Динамическая страница — это запрос к базе и сборка строки. Пример списка товаров с поиском:

Функция СписокGET(Запрос)

    Поиск = Запрос.ПараметрыЗапроса.Получить("q");

    ЗапросКБазе = Новый Запрос;
    ЗапросКБазе.Текст =
    "ВЫБРАТЬ ПЕРВЫЕ 50
    |    Номенклатура.Код КАК Код,
    |    Номенклатура.Наименование КАК Наименование
    |ИЗ
    |    Справочник.Номенклатура КАК Номенклатура
    |ГДЕ
    |    НЕ Номенклатура.ЭтоГруппа
    |    И Номенклатура.Наименование ПОДОБНО &Шаблон
    |УПОРЯДОЧИТЬ ПО
    |    Наименование";
    ЗапросКБазе.УстановитьПараметр("Шаблон", "%" + ?(Поиск = Неопределено, "", Поиск) + "%");
    Выборка = ЗапросКБазе.Выполнить().Выбрать();

    Строки = Новый Массив;
    Строки.Добавить("<!DOCTYPE html><html lang=""ru""><head><meta charset=""utf-8"">");
    Строки.Добавить("<title>Товары</title></head><body><h1>Товары</h1><ul>");
    Пока Выборка.Следующий() Цикл
        Строки.Добавить("<li><a href=""items/" + ЭкранироватьHTML(СокрЛП(Выборка.Код)) + """>"
            + ЭкранироватьHTML(Выборка.Наименование) + "</a></li>");
    КонецЦикла;
    Строки.Добавить("</ul></body></html>");

    Ответ = Новый HTTPСервисОтвет(200);
    Ответ.Заголовки.Вставить("Content-Type", "text/html; charset=utf-8");
    Ответ.УстановитьТелоИзСтроки(СтрСоединить(Строки), КодировкаТекста.UTF8, ИспользованиеByteOrderMark.НеИспользовать);
    Возврат Ответ;

КонецФункции

Функция ЭкранироватьHTML(Знач Текст)
    Текст = СтрЗаменить(Текст, "&", "&amp;");
    Текст = СтрЗаменить(Текст, "<", "&lt;");
    Текст = СтрЗаменить(Текст, ">", "&gt;");
    Текст = СтрЗаменить(Текст, """", "&quot;");
    Возврат Текст;
КонецФункции

Три приёма, на которых держится такой код:

  1. Параметры запроса, а не склейка текста. Значение из адреса уходит в УстановитьПараметр, поэтому подставить в запрос чужой фрагмент не получится.
  2. Экранирование всего, что пришло из базы. Наименование вида Кабель <3x2.5> без замены символов сломает вёрстку, а текст с тегом <script> выполнится в браузере посетителя. Амперсанд заменяют первым, иначе испортятся уже готовые &lt;.
  3. Массив строк и СтрСоединить. На сотнях строк это заметно быстрее, чем многократное Html = Html + ....

💡 Если вёрстки много, держите HTML-заготовку в общем макете типа «Текстовый документ» с метками вроде #TITLE# внутри текста и заменяйте их через СтрЗаменить. Текст макета получают так: ПолучитьОбщийМакет("ШаблонСтраницы").ПолучитьТекст().

Карточку товара по шаблону /items/{code} делают так же. Если по коду ничего не нашлось, верните Новый HTTPСервисОтвет(404) с короткой страницей «Товар не найден»: браузеры и поисковики понимают код ответа, а не текст на странице.

Формы на странице: приём POST-запроса

Разметка формы

Обычная HTML-форма отправляет данные методом POST в формате application/x-www-form-urlencoded:

<form method="post" action="feedback">
  <input name="name" required>
  <input name="phone" type="tel">
  <textarea name="text"></textarea>
  <button type="submit">Отправить</button>
</form>

В конфигураторе у шаблона /feedback нужны два метода: GET показывает форму, POST её принимает.

Разбор тела запроса

Тело приходит строкой вида name=%D0%98%D0%B2%D0%B0%D0%BD&phone=.... Разбирают его так:

Функция РазобратьФорму(Запрос)

    Поля = Новый Соответствие;
    Тело = Запрос.ПолучитьТелоКакСтроку(КодировкаТекста.UTF8);
    Для Каждого Пара Из СтрРазделить(Тело, "&", Ложь) Цикл
        Части = СтрРазделить(Пара, "=");
        Ключ = РаскодироватьСтроку(СтрЗаменить(Части[0], "+", " "), СпособКодированияСтроки.КодировкаURL);
        Значение = "";
        Если Части.Количество() > 1 Тогда
            Значение = РаскодироватьСтроку(СтрЗаменить(Части[1], "+", " "), СпособКодированияСтроки.КодировкаURL);
        КонецЕсли;
        Поля.Вставить(Ключ, Значение);
    КонецЦикла;
    Возврат Поля;

КонецФункции

Плюс заменяют на пробел до раскодирования: браузер кодирует пробел именно плюсом.

Запись и переадресация

После записи данных верните не страницу, а переадресацию с кодом 303:

Функция ОбратнаяСвязьPOST(Запрос)

    Поля = РазобратьФорму(Запрос);
    Имя = СокрЛП(Поля.Получить("name"));
    Если ПустаяСтрока(Имя) Тогда
        Возврат Новый HTTPСервисОтвет(400);
    КонецЕсли;

    // Запись в регистр сведений, который вы добавили под обращения
    // ...

    Ответ = Новый HTTPСервисОтвет(303);
    Ответ.Заголовки.Вставить("Location", "/shop/hs/site/feedback?ok=1");
    Возврат Ответ;

КонецФункции

Схема «POST → 303 → GET» защищает от повторной отправки: если посетитель обновит страницу, браузер запросит адрес ?ok=1, а не отправит форму ещё раз.

Проверяйте каждое поле на сервере: длину, обязательность, формат телефона. Атрибут required в разметке обходится одним запросом через curl.

Если форма принимает файлы (multipart/form-data), тело удобнее читать как двоичные данные через Запрос.ПолучитьТелоКакДвоичныеДанные() и разбирать по разделителю из заголовка Content-Type. Это объёмнее, поэтому для загрузки файлов часто делают отдельную страницу с JavaScript, который отправляет файл отдельным запросом.

CSS, картинки и скрипты для страницы на 1С

Браузер загружает каждый ресурс отдельным запросом. Вариантов три.

ВариантКак сделатьПлюсыМинусы
Встроить в страницу<style> и <script> прямо в HTMLНоль лишних запросовСтили грузятся заново с каждой страницей
Отдавать из 1СШаблон /static/{file}, файлы в общих макетахВсё внутри конфигурации, переносится вместе с нейКаждый запрос занимает сеанс 1С
Отдавать веб-серверомОтдельная папка в Apache или IISБыстро, кэшируется, базу не нагружаетФайлы живут отдельно от конфигурации

Для второго варианта картинку кладут в общий макет типа «Двоичные данные», а стили в текстовый макет:

Функция СтатикаGET(Запрос)

    Файл = Запрос.ПараметрыURL["file"];
    Если Файл = "style.css" Тогда
        Ответ = Новый HTTPСервисОтвет(200);
        Ответ.Заголовки.Вставить("Content-Type", "text/css; charset=utf-8");
        Ответ.УстановитьТелоИзСтроки(ПолучитьОбщийМакет("СтилиСайта").ПолучитьТекст(), КодировкаТекста.UTF8, ИспользованиеByteOrderMark.НеИспользовать);
    ИначеЕсли Файл = "logo.png" Тогда
        Ответ = Новый HTTPСервисОтвет(200);
        Ответ.Заголовки.Вставить("Content-Type", "image/png");
        Ответ.УстановитьТелоИзДвоичныхДанных(ПолучитьОбщийМакет("Логотип"));
    Иначе
        Возврат Новый HTTPСервисОтвет(404);
    КонецЕсли;
    Ответ.Заголовки.Вставить("Cache-Control", "max-age=86400");
    Возврат Ответ;

КонецФункции

Имена файлов сверяются со списком явно. Не превращайте параметр file в путь на диске сервера: так через адрес вида ../../ можно вытащить чужие файлы.

Внешние библиотеки (Bootstrap, шрифты) подключайте ссылкой на CDN, если у посетителей есть интернет. Для внутренней сети надёжнее третий вариант таблицы.

Отладка и проверка веб-страниц 1С

Отладчик

Чтобы остановиться на точке останова в обработчике HTTP-сервиса:

  1. Включите отладку в публикации. В default.vrd добавьте строку <debug enable="true" protocol="tcp" url="tcp://localhost"/> для отладки по TCP. При отладке по HTTP указывают protocol="http" и адрес сервера отладки, по умолчанию порт 1550.
  2. В Конфигураторе откройте Отладка → Подключение и в настройках автоматического подключения отметьте HTTP-сервисы.
  3. Поставьте точку останова в модуле сервиса и откройте страницу в браузере.

Отладку на рабочей публикации не включайте: пока вы стоите на точке останова, запрос висит, а посетитель видит бесконечную загрузку.

Проверка без браузера

curl показывает код ответа и заголовки, которые браузер прячет:

curl -i "http://localhost/shop/hs/site/items?q=кабель"
curl -i -X POST -d "name=Иван&phone=123" "http://localhost/shop/hs/site/feedback"

Ошибки внутри обработчика удобно писать в журнал регистрации через ЗаписьЖурналаРегистрации, оборачивая тело функции в Попытка ... Исключение. Тогда посетитель получит аккуратную страницу с кодом 500, а подробности останутся в базе.

Что означает ответ сервера

Диагностика: выберите симптом

Вкладки-страницы на управляемой форме 1С

Иногда «сделать страницы» значит разложить реквизиты формы по закладкам. Это делается в редакторе формы без кода:

  1. В дереве элементов формы добавьте группу вида «Страницы».
  2. Внутри неё добавьте нужное число групп вида «Страница» и задайте каждой заголовок.
  3. Перетащите реквизиты в нужные страницы.
  4. В свойстве «Отображение страниц» выберите вид: закладки сверху, снизу или без закладок, если переключать будете кодом.

Переключение из модуля формы:

Элементы.ГруппаСтраницы.ТекущаяСтраница = Элементы.СтраницаДоставка;

Скрыть закладку можно свойством Видимость у группы-страницы. Событие «ПриСменеСтраницы» у группы «Страницы» срабатывает, когда пользователь щёлкает по закладке. В нём удобно подгружать данные тяжёлой вкладки только при её открытии.

В типовых конфигурациях форму не правят напрямую: вкладку добавляют в расширении конфигурации. Так она переживёт обновление релиза.

Частые вопросы

Сколько лицензий занимает HTTP-сервис?
Каждый одновременный сеанс HTTP-сервиса занимает клиентскую лицензию. Включённое повторное использование сеансов в свойствах сервиса снижает их число при частых коротких запросах.
Можно ли сделать страницы на файловой базе?
Да. Публикация и HTTP-сервисы работают и с файловой базой, нужен только веб-сервер с модулем 1С на том же компьютере или с доступом к каталогу базы.
Можно ли добавить HTTP-сервис в типовую конфигурацию без снятия с поддержки?
Да, через расширение конфигурации. Для этого в публикации должна быть включена публикация HTTP-сервисов расширений.
Как открыть такую страницу из интернета?
Опубликуйте базу на веб-сервере с внешним адресом или пробросьте порт на роутере, подключите HTTPS-сертификат и ограничьте права пользователя публикации только нужными данными.
Почему не работает ссылка на сайт после переименования корневого URL?
Адрес строится из имени публикации и корневого URL сервиса. После переименования обновите конфигурацию базы данных и поправьте ссылки в HTML-шаблонах.