Пример реализации универсального REST сервиса (Avalanche — application framework for Java)

от автора

Пример реализации универсального REST сервиса (Avalanche — application framework for Java)

"Avalanche — application framework for Java" — реализация технологии стирающей различия
между вызовами локального и удаленного кода. Отказоустойчивость, масштабируемость,
модифицируемость, непрерывная доступность идут в комплекте приятными бонусами.

В статье описан пример реализации универсального REST сервиса. В основу идеи реализации сервиса положено утверждение, что FrontEnd в каждый момент времени "знает", какими данными он манипулирует и что собирается запросить у BackEnd-а. Это утверждение позволило отказаться от разработки ORM в BackEnd-е и использования JPA при реализации универсального сервиса. Сервис способен манипулировать содержимым любой таблицы БД. Изменения структуры или созданные объекты БД сразу становятся доступны методам сервиса.

В сервисе реализованы методы трех групп:

  • …/data/table/… — манипулирования содержимым таблиц БД;
  • …/data/query/… — выполнение произвольных запросов, расширяющих функциональность сервиса;
  • …/data/info/… — получения информации о структуре объектов БД.

В сервисе реализована система ошибок, которая однозначно позволяет понять причину и место возникновении ошибки. По умолчанию, поддерживается локализация сообщений ru и en. Не все сообщения могут быть локализованы, например сообщения ошибок выполнения SQL запросов, которые зависят от локализации среды выполнения.

Сервис поддерживает работу с данными в форматах XML и JSON.

Сервис реализован с использованием библиотеки Jersey 2.0.

В общем виде формат URL строки запроса сервиса выглядит так

http://host:port/name_context/map_servlet/data/type_group/{object}[?request_parameters]  [request_body]

Где:

  • name_context — имя контекста (WEB приложения);
  • map_servlet — имя маппирования сервлета Jersey в файле web.xml;
  • data — путь этого сервиса
  • type_group — тип группы запросов (table, query или info);
  • {object} — параметр запроса

Параметр запроса {object} может принимать значения:

  • для типа table — имя_схемы.имя_таблицы в формате schema.table;
  • для типа query — имя_класса.имя_метода в формате class.method, имя класса указывается без пакета, поиск класса осуществляется в пакетах, указанных в параметра packages;
  • для типа info — имя_метода в формате method;

request_parameters — параметры запроса, могут отсутствовать

request_body — тело запроса, может отсутствовать

В HTTP заголовке могут указываться параметры:

  • Accept — не обязательный, формат данных, ожидаемый в ответе на запрос, может принимать значения application/json (по умолчанию) или application/xml;
  • Content-Language — не обязательный, язык локализации, может принимать значения ru (по умолчанию) или en;
  • Content-Type — обязательный, при наличии тела запроса, может принимать значения application/json или application/xml.

Реализация сервиса

Реализация сервиса TableService.java опубликован на GitHub.

Пример конфигурационного файла сервиса avalanche-restdb-config.xml

Пример конфигурационного файла контекста restdb.xml

Ниже приведены примеры различных запросов

Примеры запросов

Пример получения записи таблицы rp.structure_type по значению первичного ключа

Запрос:

GET /restdb/rs/data/rp.structure_type?st_id=43 HTTP/1.1 Accept: application/json Content-Language: ru Host: localhost:8080

Ответ: (HTTP Status — 200)

[ { "st_id": 43, "st_name": "Test43 " } ]

Пример получения нескольких записей таблицы rp.structure_type по значению первичного ключа

Запрос:

GET /restdb/rs/data/rp.structure_type?st_id=43&st_id=42 HTTP/1.1 Accept: application/json Content-Language: ru Host: localhost:8080

Ответ: (HTTP Status — 200)

[ { "st_id": 42, "st_name": "Test42 " }, { "st_id": 43, "st_name": "Test43 " } ]

Пример получения всех записей таблицы rp.users, если это не запрещено в конфигурации сервиса

Запрос:

GET /restdb/rs/data/rp.users HTTP/1.1 Accept: application/json Content-Language: ru Host: localhost:8080

Ответ: (HTTP Status — 200)

[ { "us_last": "Петров ", "us_name": "Петр ", "us_email": "p_petrov@domain.ru " }, { "us_last": "Сидоров ", "us_name": "Сидор ", "us_email": "s_sidorov@domain.ru " }, { "us_last": "Пупкин ", "us_name": "Пуп ", "us_email": "p_pupkin@domain.ru " }, { "us_last": "Остапов ", "us_name": "Остап ", "us_email": "o.ostapov@domain.ru " }, { "us_last": "Иванов ", "us_name": "Иван ", "us_email": "i.ivanov@domain.ru " }, { "us_last": "Кириллов ", "us_name": "Кирилл ", "us_email": "k.kirillov@domain.ru " } ]

Пример получения записей таблицы rp.users, имеющих вхождение подстроки "ров" в поле us_last

Запрос:

GET /restdb/rs/data/rp.users?us_name=%ров% HTTP/1.1 Accept: application/json Content-Language: ru Host: localhost:8080

Ответ: (HTTP Status — 200)

[ { "us_last": "Петров ", "us_name": "Петр ", "us_email": "p_petrov@domain.ru " }, { "us_last": "Сидоров ", "us_name": "Сидор ", "us_email": "s_sidorov@domain.ru " } ]

Пример вставить одну запись в таблицу rp.test

Если в теле запроса будет указано несколько записей вставятся в таблицу множество записей.

Запрос:

POST /restdb/rs/data/table/rp.test HTTP/1.1 Accept: application/json Content-Language: ru Content-Length: 58 Host: localhost:8080 Content-Type: application/json  [{"ts_id":9, "ts_tszone":"2019-01-19T15:37:13.380+03:00"}]

Ответ: (HTTP Status — 200)

{ "query": "INSERT", "result": 1, "timer": 3 }

Пример вставить одну запись в таблицу rp.test

Пример, аналогичный запросу выше, но с указанием параметров вставляемой записи в строке запроса. Если в строке запроса присутствуют параметры то тело запроса в HTTP методе POST игнорируется.

Запрос:

POST /restdb/rs/data/table/rp.test?ts_id=10&ts_tszone=2019-01-19T15:37:13.380+03:00 HTTP/1.1 Accept: application/json Content-Language: ru Content-Length: 58 Host: localhost:8080 Content-Type: application/json

Ответ: (HTTP Status — 200)

{ "query": "INSERT", "result": 1, "timer": 3 }

Пример неудачной попытки вставить запись с использование метода insert класса SQL генератора TestQuery

В теле запроса допущена опечатка (намеренно), в имени поля us_last пропущена буква "s"

Запрос:

POST /gui/rs/data/query/TestQuery.insert HTTP/1.1 Accept: application/xml Content-Language: ru Content-Length: 61 Host: localhost:8080 Content-Type: application/json  [{"us_name":"3", "us_lat":"333", "us_email":"333@domain.ru"}] 

Ответ: (HTTP Status — 400)

<?xml version="1.0" encoding="UTF-8" standalone="yes" ?> <serverError> <cause> [24344@likhovskikh-vv] SYS0250E При вызове метода "execute" возникла ошибка. Ошибка: "ru.funsys.avalanche.sql.SQLException: [24344@likhovskikh-vv] SQL0021E При выполнении запроса произошла ошибка. Номер запроса: 0, запрос: INSERT INTO rp.users (us_name, us_last, us_email) VALUES (?, ?, ?).&#xD; org.postgresql.util.PSQLException: ОШИБКА: нулевое значение в столбце "us_last" нарушает ограничение NOT NULL Подробности: Ошибочная строка содержит (3 , null, 333@domain.ru )." </cause> <cоde>RST0007E</cоde> <message>При выполнение SQL запроса произошла ошибка.</message> </serverError>

Преимущества универсального сервиса по сравнению с общепринятым подходом реализации REST сервисов.

  1. Универсальный REST сервис готов для использования с любой реляционной БД.
  2. Нет зависимости от требования JPA обязательного наличия индексного идентификатора ID в каждой таблице модели данных. Коллеги, в реляционной теории БД нет понятия индексного поля таблицы ID, которое используется в качестве первичного ключа. Наличия этого поля превращает вашу реляционную БД в индексную.
  3. URI, параметры запроса, тело запроса и результат выполнения запроса формализованы.
  4. Универсальный сервис легко расширяемый, достаточно определить требуемые методы генерации SQL запросов в одном или нескольких классах SQL генераторов.
  5. Первичный ключ таблиц БД может состоять из множества полей. Поля первичного ключа доступны для модификации при соблюдении условий целостности.
  6. Отсутствие отображения программной модели объектов модели данных в БД значительно упрощает эксплуатацию, особенно в распределенной среде (например, в системах с георезервированием во множестве ЦОД).

Другие статьи по данной теме

  1. Модель приложения (Avalanche — application framework for Java)
  2. Первое приложение (Avalanche — application framework for Java)

ссылка на оригинал статьи https://habr.com/ru/post/485408/


Комментарии

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

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