Глава 1
Вводная статья прошлого года была, если можно так сказать, «Введением» в книге. Поэтому данную статью по праву можно назвать первой главой. В чем цель нашего путешествия? В конце мы должны получить полнофункциональный веб-движок и работоспособное приложение, которое на нем работает. Да-да, движок еще не готов, поэтому, в каждой статье будут использоваться только те его технические возможности, которые к моменту выхода статьи будут в нем реализованы.
Таким образом, создание нашего веб-приложения и веб-движка будет происходить «в прямом эфире» Инфостарта. Более того, поскольку это все будет Open-Source, то вы и сами сможете поучаствовать в процессе.
Вижу цель – не вижу препятствий
У вас есть сервер 1С? А сколько их у вас? Если несколько, да к тому же на разных версиях платформы, то вы хорошо себе представляете всю «прелесть» их администрирования с помощью стандартной консоли администрирования. На всякий случай напомню, что эта консоль привязана к конкретной версии 1С и устанавливается глобально на всю систему. Если вам нужно на секундочку заглянуть в сервер другой версии, вам придется выполнить ряд шаманских действий по перерегистрации консоли на другую версию.
Конечной целью нашего путешествия должно стать приложение, имеющее веб интерфейс, с нормальным json-api, с пунктом меню "убить всех", с быстрыми шаблонами "Заблокировать на ... минут" и подобным.
Даже если вы не собираетесь писать сайты на 1Script, но интересуетесь веб-разработкой в перерыве между Конфигуратором и приемами пищи, то данная книжка будет несомненно полезной и поможет уложить в голове основные подходы и практики разработки «не-на-1С».
Поехали!
Как бы ни хотелось немедленно перейти к выводу нашей первой HTML-странички, без настройки рабочего окружения нам, все-таки, никак. Кроме того, первая HTML-страничка уже появлялась в прошлой статье, и значит теперь можно заняться более серьезными делами.
В 2018 году большинство программ с открытым исходным кодом располагаются на сервисе github.com. Даже в замкнутом мире 1С уже, наверное, не осталось людей, которые ни разу не слышали про этот сервис. Это глобальная социальная сеть для программистов. Только вместо фоточек – обмениваются кодом, багрепортами, предложениями и улучшениями.
Если у вас есть программа, которую вы хотите опубликовать – делать это лучше на github.
Кроме того, в 2018 году никто в здравом уме не рискует писать программы, не сохраняя историю версий кода. Каждая правка программы может быть удачной, а может быть и нет. Всегда нужна возможность отката к предыдущему состоянию. Все это приводит нас к тому, что в рамках достижения нашей цели мы, все-таки, должны будем освоить git и github. Это не так страшно, как кажется.
Кроме того, от нас потребуется умение немного, совсем чуть-чуть, печатать в системной консоли.
Регистрируемся на github
Если у вас все еще нет аккаунта на гитхаб – ай-яй-яй. Скорее бежим это делать. Регистрация ничем не отличается от других сайтов, описывать процесс не будем.
Установка установщика
Нам потребуется установить на машину довольно много инструментов, поэтому, чтобы сэкономить время на поиск и скачивание дистрибутивов – мы поставим пакетный менеджер chocolatey, который спасет кучу нашего времени. Что такое chocolatey? Это программа, которая позволяет вам вместо того, чтобы найти сайт программы, скачать setup, запустить его и нажать Далее-Далее, просто сказать «поставь мне фотошоп» и все будет сделано само. Единственная проблема: вот именно фотошоп она не поставит, поскольку он платный, закрытый и его надо покупать у компании Adobe, а не ставить с помощью пакетных менеджеров. Однако, с бесплатными продуктами никаких проблем – можно ставить все, что душе угодно.
Итак, ставим chocolatey. Полная инструкция по установке находится по адресу https://chocolatey.org/install. Но мы сэкономим время, и напишем прямо здесь то, что нужно сделать:
Для Windows 10
- Правой кнопкой по меню «Пуск», выбрать Windows PowerShell (администратор)
- Вставить в консоль команду:
Set-ExecutionPolicy Bypass -Scope Process -Force; iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))
Для Windows более младших версий
- Правой кнопкой по меню «Пуск», выбрать Командная строка (администратор)
- Вставить в консоль команду:
@"%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe" -NoProfile -InputFormat None -ExecutionPolicy Bypass -Command "iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))" && SET "PATH=%PATH%;%ALLUSERSPROFILE%\chocolatey\bin"
После выполнения команды на компьютер будет установлена версия chocolatey.
Установка git
Если git у вас уже установлен – переходим к следующему пункту.
После установки choco не закрываем административную консоль, в ней пишем:
choco install git /NoShellIntegration
На вопрос отвечаем «Y» - то есть «да, установить». Ключик «NoShellIntegration» я рекомендую указать, чтобы git не ставил нам свой собственный встроенный клиент. Он довольно убогий, и мы поставим другой.
Установка 1Script и Visual Studio Code
Для разработки нам потребуется инфраструктура 1Script и редактор кода Visual Studio Code.
Подробная инструкция что к чему находится на инфостарте здесь: //infostart.ru/public/687869/ и видеоинструкция здесь: //infostart.ru/public/685014/ Установим редактор:
choco install visualstudiocode
И установим 1Script:
choco install onescript.cli -Source http://myget.org/F/onescript -y
Теперь, когда VSCode установлен, поставим туда расширение для поддержки языка 1С (см. инструкции по ссылкам выше). Хочу отдельно заметить, что данное расширение создано силами всего 2-х членов сообщества 1С-ников и возможности этого расширения просто поражают. Во многом оно умеет то, чего не делает Конфигуратор и EDT вместе взятые. К сожалению, полностью переехать из Конфигуратора в VSCode пока нельзя, но вы можете подключиться к проекту и исправить ситуацию.
Скачиваем веб-движок
После установки инструментов осталось скачать предмет нашей беседы – веб-движок 1Script. Поскольку он пока находится в разработке – у него нет официального дистрибутива. Он публикуется непосредственно на гитхабе в разделе «Releases» по ссылке https://github.com/EvilBeaver/OneScript.Web/releases/download/v0.1-alpha-1/osp-0.1-alpha-1.zip
Скачайте архив с движком и распакуйте его на диск. Желательно, чтобы путь к каталогу был коротким, т.к. нам предстоит его указывать в консоли. Например, его можно распаковать на D:\osweb
Если у вас версия windows младше 10-ки, то лучше, на всякий случай, обновить еще и .net framework.
choco install –y dotnet4.6.1
В итоге, у нас должны на машине оказаться:
- Git
- VSCode
- 1Script
- Веб-движок OSP.NET
Проверка систем
- Откройте чистую консоль с нуля (Пуск/Выполнить, cmd, Enter)
- Напишите git, Enter. Должен быть выдан результат работы git, а не какие-то ошибки системы
- Напишите oscript, Enter. Должен быть выдан результат работы 1Script
- Напишите opm, Enter. Должен быть выдан результат работы пакетного менеджера 1Script
Если один из этих пунктов не выполнен – попробуйте перелогиниться в систему. Иногда бывает, что переменные окружения не подхватываются сразу…
Приступаем к проекту
Я буду вести разработку на базе шаблона веб-приложения https://github.com/EvilBeaver/webapp-template. В нем уже есть готовая структура проекта, которая достаточно удобна для быстрого старта.
Переходим по ссылке на репо шаблона проекта и нажимаем кнопку «Clone or Download/Download ZIP». У вас скачается архив с шаблоном веб-приложения. Распакуйте его в любое удобное место.
У меня это будет D:\GITS\ServersConsole
Обратите внимание, мы не делаем «форк» от шаблона. Шаблон - это просто заготовка. Реальный проект на его основе нужно делать в отдельном, созданном с нуля репозитории.
Запустим Visual Studio Code и откроем наш проект через меню «Файл/Открыть папку», где укажем нашу папку с проектом ServersConsole. В левой панели vsc появятся файлы нашего проекта.
У VSCode есть удобная встроенная консоль, которая позволяет, не переключаясь в другие окна, вводить команды системы. Запустим интегрированный терминал из меню «Вид» (Ctrl+`).
В нем введем:
git init
git add –A .
В левой панели VSC у значка контроля версий должно будет появиться число файлов, содержащих изменения. Перейдем в закладку контроля версий и напишем комментарий к нашему изменению «Первичное размещение файлов проекта». После чего нажмем кнопку с галочкой.
Все, наши изменения теперь зафиксированы. Что бы мы ни натворили с кодом – мы всегда сможем вернуться к состоянию «Чистого листа».
Изучаем модуль приложения
Запуск приложения начинается с модуля «main.os» который расположен в корне исходников приложения. Откроем этот файл и посмотрим на него:
#Использовать "model"
Процедура ПриНачалеРаботыСистемы()
ИспользоватьСтатическиеФайлы();
ИспользоватьМаршруты();
КонецПроцедуры
Сейчас в нем всего одна процедура ПриНачалеРаботыСистемы, которая вызывается один раз при запуске приложения. В этой процедуре выполняется основная настройка системы. В данный момент нам интересна директива «Использовать» в начале файла.
Это одно из небольших отличий 1Script от 1С. Платформа 1С сама загружает нам все наши Справочники и Обработки в область видимости кода. В 1Script заранее нам неизвестно – что из объектов нам понадобится, поэтому мы указываем подгружаемые типы явно.
В данном случае мы указали, что нужно загрузить в область видимости классы и модули, объявленные в папке «model». Сейчас в ней ничего нет, мы наполним ее несколько позже.
Добавляем контроллер
Вернемся в закладку с деревом файлов (левая панель со значками). В дереве файлов VSC выделите папку src/controllers и создайте в ней (плюсиком «Добавить файл») файл «home.os». Это будет наш контроллер, который отвечает за обработку URL начинающихся с /Home. Если в этом месте непонятно, что такое «контроллер» - рекомендую вернуться к вводной статье //infostart.ru/public/722160/ и прочитать ее еще раз.
Итак, в нашем контроллере создадим следующий метод:
Функция Index() Экспорт
Возврат Содержимое("Привет");
КонецФункции
Это будет бизнес-логика, срабатывающая при вызове контроллера.
Понятие «Маршрута»
В предыдущей статье я уже рассказывал, что такое «Маршрут» в URL. Маршрут – это схема, по которой приложение будет откликаться на те или иные запрошенные страницы. Маршруты состоят из сегментов, разделенных символом /.
В нашем шаблоне приложения уже зарегистрирован типовой маршрут по схеме «Контроллер=Home/Действие=Index».
Это означает, что при запросе любого из адресов:
- /
- /Home
- /Home/Index
Будет вызываться контроллер Home и его метод Index, результат которого будет отправлен клиенту.
А как запустить и посмотреть?
Вот теперь самое интересное. Мы ранее скачали и распаковали на диск веб-движок. У меня это d:\osweb. Перейдем в интегрированный терминал VSCode. Там, сменим текущий каталог на подпапку src командой cd src.
Теперь, можно запустить сервер приложения. Он ищет исходники в текущем каталоге и запускается, слушая адрес localhost:5000. Запускаем:
D:\osweb\OneScript.WebHost.exe
В терминале должен появиться текст:
Подсказка: в консоли есть автодополнение. Начните набирать первые буквы, затем нажмите Tab. Система предложит подходящее название. Если таких названий несколько- нажимайте Tab до тех пор, пока не появится нужное.
Теперь откроем браузер по адресу http://localhost:5000 и увидим в нем наше приветствие. Можно поменять URL на http://localhost:5000/Home/Index и результат будет тот же.
Теперь, остановим наше приложение нажатием Ctrl+C в терминале. Это действие аналогично команде «Завершить отладку» в Конфигураторе 1С.
Маленький трюк: чтобы в консоли выполнить предыдущую команду надо нажать стрелку «вверх» на клавиатуре. История введенных команд листается стрелками Вверх и Вниз. Таким образом, чтобы перезапустить наше приложение, нужно в терминале нажать Вверх и Enter.
Так на данный момент выглядит цикл запуска/останова отладки (пока не созданы расширения для VSC специально под веб-разработку для OneScript). Т.е. мы запускаем приложение в директории с исходниками, проверяем работу в браузере, останавливаем приложение по Ctrl+C, перезапускаем по «Стрелка Вверх, Enter». В будущем для веб-приложений будет доступен пошаговый отладчик, так же, как и для обычных скриптов на 1Script.
Отображение веб-страниц
Вывод из предыдущего примера, разумеется, не является HTML-страницей. Этот способ вывода подходит, когда мы должны передать на клиент сухую текстовую информацию, например, результат вызова какого-нибудь JSON-API. Для отображения html-страниц на сцену выходит компонент MVC, скрывающийся под буквой “V”. Он же View, он же Представление.
Технически веб-движок 1Script можно дополнить различными шаблонизаторами страниц, однако, на данном этапе развития в движке существует только родной для ASP шаблонизатор Razor от фирмы Microsoft. Он весьма мощный и при этом простой в освоении.
Для включения механизма представлений нам надо немного модифицировать наш метод Index
Функция Index() Экспорт
Возврат Представление("Привет");
КонецФункции
Жирным шрифтом я выделил измененную строку кода. Снова запустим наше приложение через встроенный терминал и зайдем браузером на адрес localhost:5000. Мы увидим следующую картину:
Данная ошибка примерно обозначает то же, как если бы мы в 1С попытались открыть несуществующую форму по ее имени. Ошибка говорит, что представление с именем «Привет» не было найдено ни в одном из каталогов, в которых система ищет представления. Представления располагаются в каталоге views. Как правило, представления делаются под какое-то действие в контроллере. Контроллер отвечает за входящий запрос, а представление – за результат этого запроса.
Представления, ассоциированные с контроллером home, хранятся в подкаталоге views/home. Представления, которые не привязаны к какому-либо контроллеру, располагаются в папке views/shared.
Создадим в папке views/home файл «Привет.cshtml» следующего содержания:
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<title>Index</title>
</head>
<body>
<div>Hello World (from the view)</div>
</body>
</html>
Теперь обновим в браузере страницу по F5. В браузере будет отображена страница, созданная из нашего шаблона.
Каркас разметки представлений
Легко заметить, что в начале HTML шаблона расположена странная конструкция @Layout = null. Эта строка добавлена специально для учебного примера, в реальных страницах такое будет встречаться очень редко. Дело в том, что большинство веб-приложений имеют довольно стабильный «каркас» разметки он же «Layout». Довольно муторно в каждой странице создавать теги html, body и прочие обязательные с точки зрения стандарта, но повторяющиеся блоки. Такие вещи обычно выносят в каркас разметки, а в самих страницах формируют уже конкретные, общественно полезные элементы интерфейса.
В применяемом нами шаблоне приложения уже создан базовый каркас разметки, «раскладка», которая задает основной html страницы, ее заголовки, стили, открывающие/закрывающие теги body и пр.
Файлы раскладок хранятся в подкаталоге views/shared и начинаются с символа подчеркивания «_». Используя раскладку, мы объявляем основной «скелет» разметки интерфейса и в дальнейшем меняем только его наполнение.
Сам файл разметки имеет содержимое:
<!DOCTYPE html>
<html>
<head>
<title>@ViewBag.Title</title>
</head>
<body>
@RenderBody()
</body>
</html>
Т.е. легко заметить, что это и есть весь тот самый «обрамляющий» страницу html код. А ее осмысленное содержимое выводится в том месте, где расположен вызов метода «RenderBody()».
Если из предыдущего примера с файлом «Привет.cshtml» убрать весь обрамляющий код, а также конструкцию { @Layout = null }, то файл «Привет.cshtml» примет вид:
<div>Hello World (from the view)</div>
Содержимое страницы радикально упростилось. Осталась всего одна строка кода, описывающая содержимое одного конкретного Представления. Весь остальной код страницы будет взят из файла разметки.
Обновите страницу localhost:5000 в браузере и вы увидите тот же самый результат.
Упрощение вызова контроллера
На самом деле, каждый раз придумывать имена для View – довольно утомительное занятие. Для упрощения жизни в системе существует соглашение: «имя Представления совпадает с именем вызывающего метода контроллера».
Поскольку в нашем примере маршрут /home/index ведет к контроллеру home и его методу Index, то представление, которое отображает результат данного вызова, по-умолчанию имеет имя “Index.cshtml”
Если имя представления в вызове метода «Представление()» не будет указано, то будет осуществлен поиск файла .cshtml с тем же именем, что и текущий метод контроллера.
Иными словами, достаточно переименовать наш тренировочный файл «Привет.cshtml» в «Index.cshtml», как у нас появляется однозначная связка метода-обработчика с представлением. И тогда, можно не задавать имя представления в самом контроллере, а оставить все по-умолчанию.
// вызов Представление() без указания имени. Будет искать /views/home/Index.cshtml
Функция Index() Экспорт
Возврат Представление();
КонецФункции
По традиции, изменение показано жирным шрифтом.
Подведем итог
Маршрут URL определяет – где находится точка входа в каждый конкретный http-вызов нашего приложения.
Контроллеры располагаются в папке controllers, их имена файлов являются (как правило, но не обязательно) частью маршрута по которому они вызываются. Экспортные методы контроллеров – это «Действия», они тоже являются (как правило, но необязательно) частью URL.
Представления располагаются в папке views/{имя контроллера} или views/shared, если они не относятся к какому-либо конкретному контроллеру.
Имя файла представления желательно делать таким же, как имя метода, которым данное представление формируется.
Разработка интерфейса
Разработку интерфейса мы будем вести с помощью довольно простого фреймворка «semantic UI». Для него существует в сети довольно много шаблонов, и мы сможем тырить оттуда идеи верстки. Новомодные React и Vue оставим на потом, это целый отдельный мир и лезть туда – это полезно, но пока не стоит забивать себе голову.
Тем не менее, я хочу отдельно заметить, что в сети огромное количество уже готовых компонентов интерфейса. На любой вкус и цвет. Практически никто сейчас не пишет элементы интерфейса с нуля. Однако, в рамках этой книги мы займемся именно этим – созданием UI на низком уровне. Это потому, что наша цель – описать фреймворк разработки, его составные части и принцип работы. Это лучше делать на детальном уровне, чтобы принцип функционирования был понятен. Проще говоря, чтобы использовать современный UI типа React – надо сначала выяснить, куда его вставлять. Рассказ про React и современный web UI - это отдельная книжка, которая уведет нас от основной темы очень далеко.
Итак, наш интерфейс будет содержать боковую панель управления, верхнее главное меню и центральную область с содержимым сайта. Это все мы разместим в каркасе разметки, поскольку эти компоненты будут на экране всегда.
Откроем файл /views/shared/_mainLayout.cshtml и изменим его следующим образом:
<!DOCTYPE html>
<html>
<head>
<title>@ViewBag.Title</title>
<link rel="stylesheet" h_ref="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.2.13/semantic.min.css"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<link rel="stylesheet" href="~/default.css"/>
</head>
<body>
<!-- top menu -->
<div class="ui inverted huge borderless fluid menu">
<a class="header item">Odminus</a>
<div class="right menu">
<a class="item">Настройки</a>
<a class="item">Помощь</a>
</div>
</div>
<div class="ui grid">
<div class="row">
<div class="three wide column" id="sidebar">
<div class="ui secondary vertical fluid menu">
<a class="item active">Центральные серверы</a>
<div class="ui hidden divider"></div>
<a class="item">Информационные базы</a>
<a class="item">Администраторы</a>
<a class="item">Блокировки</a>
</div>
</div>
<div class="twelve wide column" id="content">
@RenderBody()
</div>
</div>
</div>
</body>
</html>
Обратите внимание, в примере кода пришлось исказить атрибут href, поскольку парсер Инфостарта заменяет такой текст на свой собственный редирект. (если администрация слышит - парни, запишите баг). В реальном коде замените в скопированном тексте h_ref на href.
Кроме того, в папке wwwroot создадим файл «default.css» следующего содержания:
body {
display: relative;
}
#sidebar {
height: 100vh;
background-color: #f5f5f5;
padding: 0px;
margin-top: -10px;
}
#sidebar .ui.menu {
margin: 2em 0 0;
font-size: 16px;
padding-left: 10px;
}
#sidebar .ui.menu > a.item {
color: #337ab7;
border-radius: 0 !important;
}
#sidebar .ui.menu > a.item.active {
background-color: #337ab7;
color: white;
border: none !important;
}
#sidebar .ui.menu > a.item:hover {
background-color: #4f93ce;
color: white;
}
#content > .ui.grid {
padding-right: 4em;
padding-bottom: 3em;
}
#content h1 {
border-bottom: 1pt solid #eee;
padding-bottom: 3pt;
}
И что получилось?
Получилась заготовка интерфейса. Пока мы не знаем, какие разделы в нем будут, а можем только предполагать. Мы определили основную раскладку страниц сайта и компоненты, объявили боковую и верхнюю панель, набросили в них какие-то пункты предполагаемых меню и вывели в основное содержимое результат метода контроллера Home.Index().
Разработка модели
Под «Моделью» (буква М в аббревиатуре MVC) понимают данные, с которыми работает пользователь. Иными словами, это модель предметной области, ее отражение в виде программного кода. Модель также отвечает за обработку данных, их непротиворечивость и т.п. В бухгалтерском приложении, например, модель содержит контрагентов, налоговые ставки, план счетов, остатки и т.п. Ничего не напоминает?
Любое приложение 1Script по аналогии с приложением 1С содержит общие модули и пользовательские типы. Общий модуль – это прямой аналог общего модуля из 1С. Пользовательские типы немного отличаются. В 1С мы не можем напрямую создать пользовательский тип. Мы можем только отнаследоваться от предлагаемых базовых типов «Справочник», «Документ», «Обработка» и пр. В 1Script нет базовой платформы с прикладными бизнес-объектами, поэтому пользовательские типы больше похожи на классические языки программирования. Мы сами объявляем некий тип (класс) и потом используем экземпляры этого типа, создавая их через оператор «Новый».
Более подробно механизм модулей и классов описан на сайте http://oscript.io/docs/page/package-loader. Мы не будем до поры останавливаться на нем, на данный момент подробный принцип работы для нас не актуален.
Итак, классы и модули модели. Шаблон приложения настроен таким образом, что загружает модель из подкаталога src/model. Сам подкаталог model содержит 2 папки: classes и modules. Любой файл с кодом *.os, который мы положим в папку model/modules станет общим модулем приложения. Файл должен именоваться по принципам именования переменных.
Соответственно, любой файл с кодом, который мы положим в папку classes станет пользовательским типом, который можно будет создавать через «Новый».
Наша консоль будет работать с сервером администрирования RAS/RAС, который позволяет не привязываться к конкретной версии администрируемого кластера серверов 1С.
Кроме того, я думаю, что в рамках данной серии статей мы не будем описывать полноценную работу с RAC, поскольку она является достаточно новой для 1С-ников и сам по себе код вызова RAC будет отвлекать своей непривычностью от основной задачи – построения веб-приложения. Поэтому, тонкости работы с RAC я в тексте постараюсь опускать. Готовый работающий код будет лежать на github и его можно будет изучить там, без увеличения объема статьи.
Итак, наша задача – иметь под рукой список работающих серверов 1С с возможностью подключиться к каждому из них и провести над ними некоторые административные действия.
Значит, нам понадобится сущность «Администрируемый сервер» с которого и начинается все дальнейшее администрирование. У него будет пользовательское наименование, сетевое имя и тип протокола «RAC» или «COM». COM пригодится для администрирования старых версий платформ. Их можно, например, зарегистрировать под разными именами COM-классов, как описано в статье //infostart.ru/public/685924/.
Далее, нам потребуется список таких серверов и возможность добавлять, править и удалять сервера из этого списка. Все это задачи базы данных. В 1Script есть готовый пакет для работы с базами данных https://github.com/oscript-library/sql, однако, на данный момент мы не будем отвлекаться на детали работы с БД, а саму базу заменим файлом json. Благо, объем предполагаемых к хранению данных – очень маленький.
Создание и хранение списка серверов
Пусть за управление списком серверов у нас отвечает общий модуль ЦентральныеСерверы.
Тогда, в модуле ЦентральныеСерверы у нас получаются методы
- ПолучитьСписок()
- Создать ()
- Записать (Элемент)
- Удалить(Элемент)
Предположим, файл с данными о серверах у нас будет лежать в текущем каталоге под именем appData.json. Для работы с форматом json будем использовать стандартный пакет json из библиотеки 1Script.
Добавление пакета в проект
Каждый раз, когда мы хотим что-то написать, удобно задавать себе вопрос: а вдруг кто-то уже написал подобное? В данном случае, пакет для работы с JSON уже есть в библиотеке 1script. Этот пакет – порт разработки Александра Переверзева с инфостарт. В 1Script она перекочевала с минимальными доработками, путем копирования и вставки. Это ведь один и тот же язык, поэтому запускать готовый код 1С в среде 1script довольно удобно.
1Script имеет облачное хранилище готовых пакетов и пакетный менеджер, который позволяет быстро и просто установить нужный пакет для нужд разработки. После установки пакета наша программа получит т.н. «зависимость» от внешнего пакета. Этот факт надо отразить в манифесте приложения, для того, чтобы было понятно – какие сторонние библиотеки нашей программе нужны для работы.
Зависимости отражаются в специальном файле - Манифесте приложения – с именем packagedef. Этот файл находится в корне нашего каталога исходников (src). Откроем его.
Описание.Имя("odminus")
.Версия("0.1.0")
.ЗависитОт("json")
;
Жирным шрифтом выделена строчка, которую я туда добавил. Теперь, другие разработчики смогут увидеть – какие зависимости нужны нашей программе.
Откроем встроенный терминал vsc (меню «Вид\Интегрированный терминал» или Ctrl+`) В нем должен быть открыт подкаталог src нашего репо. Если это не так, перейдите в каталог src командой «cd src».
Далее, находясь в каталоге src пишем:
opm install -l
Эта команда означает приказ пакетному менеджеру opm установить все пакеты зависимостей из packagedef. Ключик –l означает, что пакет надо установить локально – в текущий каталог, а не в системное хранилище пакетов 1Script.
После выполнения этой команды пакетный менеджер скачает нужную библиотеку из облака и разместит ее в подкаталоге oscript_modules. В нашей программе станет доступна библиотека json. Поместим это изменение в хранилище git. (В боковой панели слева кнопка «Управление версиями»)
Создание модуля управления серверами
Создадим файл ЦентральныеСерверы.os в папке src/model/modules. Программируется этот файл также, как и любой модуль в 1С.
В начале файла добавляем директиву импорта:
#Использовать json
После указания директивы импорта классы, объявленные в библиотеке «json» станут нам доступны, и мы сможем ими пользоваться.
Создаем остальной код модуля:
#Использовать json
Перем мПутьКФайлуНастроек;
Функция СодержимоеНастроек()
Файл = Новый Файл(мПутьКФайлуНастроек);
Если Не Файл.Существует() Тогда
Содержимое = "[]";
Иначе
Текст = Новый ЧтениеТекста(мПутьКФайлуНастроек);
Содержимое = Текст.Прочитать();
Текст.Закрыть();
КонецЕсли;
Возврат Содержимое;
КонецФункции
Функция ПолучитьСписок() Экспорт
СписокСерверов = Новый ТаблицаЗначений;
СписокСерверов.Колонки.Добавить("Идентификатор");
СписокСерверов.Колонки.Добавить("СетевоеИмя");
СписокСерверов.Колонки.Добавить("Порт");
СписокСерверов.Колонки.Добавить("Режим");
Парсер = Новый ПарсерJSON;
Данные = Парсер.ПрочитатьJSON(СодержимоеНастроек(),,,Истина);
Если ТипЗнч(Данные) <> Тип("Массив") Тогда
Возврат СписокСерверов;
КонецЕсли;
Для каждого объект Из Данные Цикл
ЗаполнитьЗначенияСвойств(СписокСерверов.Добавить(), объект);
КонецЦикла;
Возврат СписокСерверов;
КонецФункции
///////////////////////////////////////////////////
мПутьКФайлуНастроек = "appData.json";
Контроллер создания администрируемых серверов
Начнем с определения маршрутов к управлению серверами. Пусть шаблон доступа к управлению сущностями у нас имеет вид {контроллер}/{действие}/{идентификатор?} Это позволит нам осуществлять следующие HTTP-операции с агентами кластеров 1С:
- /agents
- /agents/create
- /agents/edit/srv-1c-0001
- /agents/delete/srv-1c-0001
За управление администрируемыми серверами будет отвечать контроллер agents.os. Добавим его в папку controllers.
Начнем с получения простого списка имеющихся серверов:
Функция Index() Экспорт
ТаблицаАгентов = ЦентральныеСерверы.ПолучитьСписок();
Возврат Представление(ТаблицаАгентов);
КонецФункции
Здесь я использую вариант метода Представление с одним параметром, в который я передаю Модель – таблицу зарегистрированных агентов. Модель – это произвольный объект, который затем будет доступен внутри Представления в виде свойства Model.
Создадим это представление. В папку views/agents добавим файл Index.cshtml
<h1>Агенты администрируемых серверов</h1>
<table class="ui basic selectable table">
<thead>
<tr>
<th>Идентификатор</th>
<th>Сетевое имя</th>
<th>Порт</th>
<th>Режим</th>
</tr>
</thead>
<tbody>
@foreach(var row in Model) {
<tr>
<td><a asp-action="edit" asp-route-id="@row.Идентификатор">@row.Идентификатор</a></td>
<td>@row.СетевоеИмя</td>
<td>@row.Порт</td>
<td>@row.Режим</td>
</tr>
}
</tbody>
</table>
Жирным шрифтом выделено самое интересное – цикл, который перебирает строки ТаблицыЗначений и выводит их в HTML-разметку.
Синтаксис выражений шаблонизатора Razor мы разберем в последующих главах, а пока просто запомним, что выражения Razor начинаются с символа «собаки».
Если сейчас запустить приложение и перейти по адресу /agents – то мы увидим пустую таблицу. Добавим переход к разделу агентов в главное меню. В _mainLayout.cshtml заменим одну из заглушек на следующий код:
<div class="ui secondary vertical fluid menu">
<a class="item active" asp-controller="agents" asp-action="index">Агенты серверов</a>
<div class="ui hidden divider"></div>
<a class="item">Информационные базы</a>
<a class="item">Администраторы</a>
<a class="item">Блокировки</a>
</div>
Я изменил строку, выделенную жирным шрифтом. В ней вместо традиционного URL гиперссылки использованы специальные атрибуты asp-controller и asp-index. Это вспомогательные теги, позволяющие оперировать компонентами MVC из файлов разметки. Они формируют корректный целевой URL, базируясь на карте маршрутов нашего приложения. В принципе, можно и жестко прописать URL «/agents», но это менее гибко и может сломаться, если мы в будущем захотим изменить схему маршрутизации.
Добавление/редактирование агента
Добавим в представлении views/agents/Index перед таблицей кнопку создания нового агента:
<div id="cmdbar" class="ui buttons">
<a asp-action="add" class="ui primary button">Добавить</a>
</div>
В сам контроллер agents добавим экспортный метод Add
Функция Add() Экспорт
Возврат Представление("Item");
КонецФункции
И, наконец, создадим само представление Item.cshtml в папке views/agents:
<h2 class="ui dividing header">Агент кластера</h2>
<form asp-action="add" class="ui form">
<p class="field">
<label for="Идентификатор">Идентификатор</label>
<input name="Идентификатор"></input>
</p>
<p class="field">
<label for="СетевоеИмя">Сетевое имя</label>
<input name="СетевоеИмя"></input>
</p>
<p class="field">
<label for="Порт">Порт</label>
<input name="Порт"></input>
</p>
<button type="submit" class="ui primary button">Сохранить</button>
</form>
В данной форме используется уже знакомый нам атрибут “asp-action”, который задает адрес, на который форма перешлет данные при нажатии кнопки «Сохранить».
Данные формы будут пересланы методом POST. Теперь, если перезапустить приложение и зайти браузером на адрес /agents/add, то можно увидеть нашу форму. Проблема лишь в том, что метод add не умеет сохранять полученные данные. Исправим это:
Функция Add() Экспорт
Если ЗапросHttp.Метод = "POST" Тогда
ТЗ = ЦентральныеСерверы.ПолучитьСписок();
Элемент = ТЗ.Добавить();
ЗаполнитьДанные(Элемент);
ЦентральныеСерверы.Записать(ТЗ);
Возврат Перенаправление("/agents/index");
КонецЕсли;
Возврат Представление("Item");
КонецФункции
Процедура ЗаполнитьДанные(Знач Элемент)
ДанныеФормы = ЗапросHttp.ДанныеФормы;
Элемент.Идентификатор = ДанныеФормы["Идентификатор"];
Элемент.СетевоеИмя = ДанныеФормы["СетевоеИмя"];
Элемент.Порт = ДанныеФормы["Порт"];
Элемент.Режим = "RAS";
КонецПроцедуры
Обратите внимание на коллекцию ДанныеФормы. Ключами в ней являются имена полей HTML. Каждый элемент <input> в нашей форме имеет атрибут «name». Именно эти имена становятся ключами коллекции ДанныеФормы, а значениями – введенные пользователем значения полей.
Полноценный CRUD
Create-Read-Update-Delete – так расшифровывается абракадабра в подзаголовке выше. Сейчас у нас есть первые 2 пункта, осталось добавить вторые два: удаление и редактирование. Для этого проведем небольшой рефакторинг нашей «Формы элемента» - представления Item.
<form asp-action="@ViewContext.RouteData.Values["action"]" asp-route-id="@Model?.Идентификатор" class="ui form">
Сначала заменим адрес, на который форма отправляет данные. В случае с добавлением у нас было только одно действие – «add». Теперь форма должна обрабатывать также и действие edit. Удобнее всего вытащить имя текущего действия из текущего контекста представления (Свойства ViewContext.RouteData внутри Razor) Это свойство связывает представление с текущим HTTP-вызовом. И мы можем сформировать разный HTML в зависимости от того, по какому адресу нас вызвали. Кроме того, в качестве параметра id маршрута добавим идентификатор текущей записи.
Далее, доработаем форму. Вставим в качестве значений полей обращение к Модели. Поскольку в случае с add мы не передавали модель, то свойство Model будет равно null. Поэтому в коде мы применим условный оператор ? для проверки на null.
<p class="field">
<label for="Идентификатор">Идентификатор</label>
<input name="Идентификатор" value="@Model?.Идентификатор"/>
</p>
<p class="field">
<label for="СетевоеИмя">Сетевое имя</label>
<input name="СетевоеИмя" value="@Model?.СетевоеИмя"/>
</p>
<p class="field">
<label for="Порт">Порт</label>
<input name="Порт" value="@Model?.Порт"/>
</p>
Забавные знаки вопроса в коде – это синтаксический сахар C#. В данном случае, код @Model?.Идентификатор на языке 1С будет выглядеть примерно так: ?(Модель <> Неопределено, Модель.Идентификатор, “”)
Осталось добавить новое действие Edit в контроллер agents и немного доработать модель, ЦентральныеСерверы, сохраняющую данные.
Доработка модуля ЦентральныеСерверы:
Процедура Записать(Знач ТЗ) Экспорт
Текст = Новый ЗаписьТекста(мПутьКФайлуНастроек);
Запись = Новый ПарсерJSON;
Текст.Записать(Запись.ЗаписатьJSON(ТЗ));
Текст.Закрыть();
мСодержимое = Неопределено;
КонецПроцедуры
///////////////////////////////////////////////////
мПутьКФайлуНастроек = "appData.json";
Добавленный метод-действие Edit в контроллере agents:
Функция Edit() Экспорт
Идентификатор = ЗначенияМаршрута["id"];
Если Идентификатор = Неопределено Тогда
Возврат Перенаправление("/agents/index");
КонецЕсли;
ТЗ = ЦентральныеСерверы.ПолучитьСписок();
Элемент = ТЗ.Найти(Идентификатор, "Идентификатор");
Если Элемент = Неопределено Тогда
Возврат КодСостояния(404);
КонецЕсли;
Если ЗапросHttp.Метод = "POST" Тогда
ЗаполнитьДанные(Элемент);
ЦентральныеСерверы.Записать(ТЗ);
Возврат Перенаправление("/agents/index");
Иначе
// Передаем в представление "модель" - Элемент
Возврат Представление("Item", Элемент);
КонецЕсли;
КонецФункции
Итого, получается, что мы теперь можем обрабатывать адреса:
- /agents
- /agents/add
- /agents/edit/buh
и наполнять список агентов кластеров серверов 1С.
Подведение итогов и раздача слонов
В рамках данной статьи мы настроили рабочее окружение разработчика: установили менеджер пакетов chocolatey, систему контроля версий git, редактор Visual Studio Code.
Создали работающий прототип веб-приложения, управляющего кластерами серверов 1С. В этом веб-приложении реализована основная разметка интерфейса и операции управления агентами серверов администрирования по технологии RAS/RAC (https://its.1c.ru/db/v8311doc#bookmark:cs:TI000000189).
Мы освоили создание новых контроллеров, методов-действий и представлений, формируемых методами-действиями.
Не реализованной осталась команда /agents/delete/<id>, и это остается в качестве домашнего задания. Конечный результат по данной главе доступен на гитхаб: https://github.com/EvilBeaver/osp-articles.
Первый человек, приславший качественный работающий pull-request с операцией «delete» получит от меня 20 стартмани.
Акция не распространяется на гиков, знакомых со мной лично. Я знаю, что вы умеете это делать. :).
Спасибо всем, кто дочитал до конца. Продолжение следует…