Ormless. Второе звено без ORM

Данный проект является экспериментом. Здесь предыстория и небольшой анализ ситуации.
Девиз проекта:

Only an gormless fool will begin to place business logic in stored procedures

Репозиторий: GitHub

Концепт

Концепт Ormless

Работает это так:

  1. Запускается приложение Ormless на сервере. При запуске оно считывает все странички с JavaScript-скриптами и кладёт их в переменную.
  2. Пользователь открывает страничку. Веб-сервер получает соответствующую команду и отдаёт из переменной страничку.
  3. Страница запрашивает данные, для чего отправляет API-команду на сервер.
  4. Сервер получает API-команду, проверяет авторизацию. Если всё нормально, команда сравнивается с имеющимся на сервере описанием этой команды — проверяются типы параметров и их содержимое.
  5. Если всё нормально — команда преобразуется в SQL-команду запуска хранимки. Команда отрабатывает, результат заворачивается в JSON.
  6. JSON передаётся на браузер как ответ.
  7. Переходим к пункту 2. Запись данных по тому же сценарию.

Разработка ведётся в двух местах:

  1. Разработка интерфейса и прочего взаимодействия с пользователем (JavaScript).
  2. Разработка БД и реализация бизнес-логики (один из SQL).

Как пользоваться

  1. В папке с программой необходимо создать папку (например pages) с веб-страничками (*.htm, *.css, *.js), картинками и другими медиафайлами.
  2. Заполнить файл настроек веб-сервера, файл config.yml.
  3. Заполнить файл-описание команд API command_parameters.json. Соответствующие хранимки должны быть в базе данных.
  4. Запустить приложение.

Ниже всё расписано более подробно.

Файл настроек

Рассмотрим содержимое файла настроек:

WebPublic:
    DomainName: lvh.me
    HttpPort: 80
    HttpIp: localhost
    Https:
        Enabled: false
        Port: 443
        Ip: localhost
        Provider: letsencrypt

        SubscriberEmail: info@lvh.me
        CertFolder: ssl

        CertPemFileName: ssl/cert.pem
        KeyPemFileName: ssl/key.pem

WebPagesFolder: pages
JsSettingsPath: /js/_settings.js

CommandPathPrefix: /cmd/
ParametersCountLimit: 30
CommandParametersFileName: command_parameters.json

OAuthVerificationCodePath: /cmd/oauth_verification_code
OAuthYandex:
    ClientId: aaa
    ClientSecret: bbb
OAuthGoogle:
    ClientId: ccc
    ClientSecret: ddd
OAuthGitHub:
    ClientId: eee
    ClientSecret: fff

DbConnectionString: sqlserver://user:password@localhost?database=test&connection+timeout=30

Подробнее о каждом параметре.

DomainName

Адрес приложения снаружи или имя сайта. Нужен для генерации ссылок, создании сертификата SSL и проч. Если публичный (внешний) порт отличается от стандартного, указываем здесь. Например lvh.me:5000. Протокол подставит программа. Если SSL используется — https://, иначе http://.

HttpPort

Указывает какой порт слушать. Из-за особенностей хостинга может не совпадать с публичным.

HttpIp

Указывает IP для прослушивания. Из-за хостинга может потребоваться указать что-то отличное от localhost.

Https

Веб-сервер поддерживает работу с SSL. Включается параметром Enabled. Указываем также IP и Port для прослушивания. Есть два режима работы: letsencrypt — если будем использовать автопродляемый бесплатный сертификат от Let’s Encrypt и custom — если сертификат покупной.

В первом случае указываем почту для обратной связи SubscriberEmail и папку CertFolder, в которую будут записаны сертификаты и закрытый ключ.

Во втором случае указываем пути файлов — сертификата CertPemFileName и закрытого ключа KeyPemFileName в формате PEM. Если у Вас другой формат — разделите их по файлам в текстовом редакторе.

WebPagesFolder

Имя папки с файлами веб-страниц и ресурсами. Файлы из этой папки будут доступны снаружи по тем же адресам, что и внутри папки. Кроме файла index.html. Например: файл, который лежит в папке по адресу \img\aaa.png снаружи будет доступен по адресу https://domain.name/img/aaa.png.

JsSettingsPath

Это относительный от доменного имени путь (снаружи) до генерируемого во время запуска веб-сервера JS-файла с полезными константами для формирования веб-страниц на клиенте. Пример содеримого данного файла:

const THIS_APP_URL = "http://lvh.me:9000";
const OAUTH_YANDEX_URL = "https://oauth.yandex.ru/authorize?client_id=xxx&response_type=code&state=yandex%token";
const OAUTH_GOOGLE_URL = "https://accounts.google.com/o/oauth2/v2/auth?access_type=offline&client_id=yyy.apps.googleusercontent.com&redirect_uri=...";
const OAUTH_GITHUB_URL = "https://github.com/login/oauth/authorize?client_id=zzz...";
  • THIS_APP_URL — это собственный адрес.
  • OAUTH_YANDEX_URL, OAUTH_GOOGLE_URL, OAUTH_GITHUB_URL — это ссылки на страницы авторизации соответствующего сервиса авторизации.

CommandPathPrefix

Все адреса API-команд базы данных форматированы так:
Доменное имя сайта + префикс команды + имя команды + "?" + GET-параметры (если вызов GET-методом). Пример: http://lvh.me:9000/cmd/entity.category_list?p=11

ParametersCountLimit

Максимальное количество параметров в API-команде, для предварительной проверки.

CommandParametersFileName

Имя файла с описаниями API-команд. См. ниже.

OAuthVerificationCodePath

URL команды верификации OAuth. Это адрес для Callback-вызовов серверов OAuth. В личных кабинетах следует указывать адрес сайта + это значение. Из примера выше: http://lvh.me/cmd/oauth_verification_code.

OAuthYandex, OAuthGoogle, OAuthGitHub

Настройки OAuth для авторизации на соответствующем сервисе авторизации. Содержат по две настройки:

  • client_id — идентификатор клиента, полученный при регистрации на соответстувющем сервисе.
  • client_secret — это пароль или секрет, также выданный при регистрации.

DbConnectionString

Строка подключения к базе данных.

Файл описания API-команд

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

[
    {
        "cmd_name": "#USER_REGISTER#",
        "db_proc_name": "Entity.UserRegister",
        "call_method": "ORMLESS",
        "description": "Запись пользователя, прошедшего регистрацию через сервис OAuth",
        "parameters": [
            { "name" : "user_name", "type" : "string", "description": "Веб сервер передаёт здесь ФИО пользователя или ник" }
            ,{ "name" : "user_email", "type" : "string", "description": "Веб сервер передаёт здесь адрес электронной почты" }
            ,{ "name" : "ext_id", "type" : "string", "description": "Внешний идентификатор в сервисе авторизации OAuth" }
            ,{ "name" : "oauth_service_name", "type" : "string", "description": "Наименование сервиса авторизации" }
        ]
    }
    ,{
        "cmd_name": "entity.category_list",
        "db_proc_name": "Entity.CategoryList",
        "call_method": "GET",
        "description": "Процедура возвращает список элементов дерева категорий",
        "parameters": [
            { "name" : "p", "type" : "int", "default" : "0", "description": "Идентификатор родительского элемента. Если 0 - то нужно вернуть элементы верхнего уровня." }
        ]
    }
]

cmd_name

Все команды имеют имя, как составная часть адреса команды. Есть также команды для внутреннего пользования, ведь веб-сервер Ormless тоже использует БД. Вот названия необходимых ему команд:

  • #USER_REGISTER# — запись пользователя при успешной авторизации через сервис OAuth. Вызвать снаружи эту команду нельзя.

db_proc_name

Название хранимки в базе данных.

call_method

Метод вызова данной команды:

  • GET — вызов снаружи HTTP-методом GET
  • POST — вызов снаружи HTTP-методом POST
  • ORMLESS — при внутреннем вызове веб-сервера

description

Текстовое описание процедуры, как элемент документации.

parameters

Массив параметров процедуры. Каждый элемент содержит:

  • name — имя параметра хранимой процедуры.
  • type — тип значения параметра.
  • default — значение по умолчанию. Указывать необязательно. Если параметр не передан, то будет использоваться указанное значение. Если параметр не передан и это значение не указано — веб-сервер вернёт ошибку до вызова хранимой процедуры.
  • description — описание параметра, как элемент документации.

Параметры могут быть следующих типов:

  • bool
  • int
  • uint
  • float
  • string

Диапазоны значений всех числовых параметров соответствуют размеру переменной в 64 бит.

Ограничения

  1. В программе использован драйвер для БД Microsoft SQL Server. При использовании другой БД программу необходимо перекомпилиовать с соответствующим драйвером.

Возможные улучшения

  1. Скомпилировать приложение с драйверами всех баз, или с одним из универсальных драйверов и вынести это в настройку.
  2. Возможно имеет смысл давать назначать для API-команд не только префикс, а URL целиком.
  3. Возможно нужно добавить тип переменных date.
  4. Добавить логирование по ошибкам, чтобы потом статистику в админку выводить. Может быть стоит хранить в БД это.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *