В этой статье, я хочу поделиться своим подходом в организации клиент-серверного взаимодействия, в одностраничных браузерных приложениях с серверной частью на Java.
Сокращённо, я называю этот подход «Json Remote Service Procedure Call» — JRSPC. (Не очень благозвучно, возможно, но из песни слова не выкинешь.)
Применение jrspc — позволяет отказаться от использования слоёв определений интерфейсов сервисов на клиенте и сервере, что сокращает количество кода, упрощает его рефакторинг, и снижает вероятность появления ошибок.
При использовании этого подхода — мы можем писать только код, отвечающий за бизнес-логику,
не нужаясь в дополнительном коде, при определении нового бизнес-метода.
Например, на сервере, определение бизнес-метода выглядит так:
а его вызов на клиенте — так:
Больше, при определении метода, нигде, никакого кода не пишется.
На транспортном уровне, jrspc — использует json-rpc, с возможностью указывать в вызове не только метод, но и сервис. Поэтому, такой json-rpc можно было бы назвать json-rspc (s-service).
Если бы на него существовала спецификация, то она была бы похожа на спецификацию json-rpc 2.0, за исключением того, что в объекте запроса было бы добавлено поле «service», а поле «id» — было бы не обязательным, и в ответе — необязателен errorCode.
Для демонстрации, я написал простое демо-приложение, в котором реализуются функциональности регистрации, логина, и изменения данных и прав пользователя.
Клиентская часть этого приложения — написана на фреймворке AngularJS.
Для оформления используется Bootstrap.
В серверной части — Spring.
В качестве реализации объекта json, используется
Клиентская часть состоит из трёх файлов:
ajax-connector.js.
Реализация механизма запросов к серверу, инкапсулированная в объекте
(Префикс ajax — используется, чтобы отличать его от вебсокетного ws-connector.js, которым он может быть заменён, без изменения кода user-controller.js.)
user-controller.js
Здесь находится бизнес-логика приложения, инкапсулированная в функции
application.html
Графический интерфейс приложения с логикой блокировки элементов.
Как видим, в представлении скриптового кода, удалённый сервер — выглядит как объект Server, который должен быть проинициализирован url'ом.
Через этот объект, мы можем обращаться к любому компоненту на сервере и вызывать любые его методы, таким способом:
Ответы или ошибки — приходят в соответствующие коллбэки.
Добавление нового сервиса или метода на сервере — никак не затрагивает клиентский код, и мы можем вызывать эти сервисы и методы сразу, после того как они появились в серверном коде.
Естественно, сказав «любому и любые» — я немного отошёл от истины.
На самом деле, как удалённые сервисы, вызываться могут только классы, производные от
Для ограничения прав доступа к методам — используется аннотация
Так, например, метод, аннотированный
Весь серверный «фреймворк», если можно так выразиться, занимает меньше 9 кб., и состоит из шести классов, два из которых — уже знакомые нам аннотации: Remote и Secured, а также AbstractService —
абстрактный класс, от которого должны наследоваться все сервисы, и CommonServiceController
В его метод
Далее, находится компонент, по имени сервиса, и на нём, после проверки прав доступа, рефлективно, вызвается указанный метод.
User (entity), для хранения данных о пользователе, и UserManager, для операций с объектом
Бизнес-логика реализована в двух сервисах: TestUserService — сервис с методами для регистрации, логина, и редактирования данных, и TestAdminService — сервис с методами для удаления юзера, и изменения его роли.
Код написан максимально self-explanatory, поэтому надеюсь, что разобраться в нём будет легко.
Код демо-приложения на Гитхабе.
В следующей статье, я планирую написать, как, на базе данного подхода, можно организовать клиент-серверное взаимодействие через вебсокеты, и как, на сервере, из вебсокетного контекста, достать сессию http.
Update2:
Update1 — перемещён в тело статьи.
Сокращённо, я называю этот подход «Json Remote Service Procedure Call» — JRSPC. (Не очень благозвучно, возможно, но из песни слова не выкинешь.)
Применение jrspc — позволяет отказаться от использования слоёв определений интерфейсов сервисов на клиенте и сервере, что сокращает количество кода, упрощает его рефакторинг, и снижает вероятность появления ошибок.
При использовании этого подхода — мы можем писать только код, отвечающий за бизнес-логику,
не нужаясь в дополнительном коде, при определении нового бизнес-метода.
Например, на сервере, определение бизнес-метода выглядит так:
@Component("testService")
public class TestService{
@Remote
public String testMethod(Long userId, String role, boolean test,
List<User> users, User user){
...
return "ok";
}
}
а его вызов на клиенте — так:
var params = [userId, role, true, [{id:1, login:"111"}, {id:2, login:"222"} ], {id:3, login:"333"}]
Server.call("testService", "testMethod", params, sucessCallback, errorCallback, controlWhichWillDisabledUntilResponse);
Больше, при определении метода, нигде, никакого кода не пишется.
Как это работает
На транспортном уровне, jrspc — использует json-rpc, с возможностью указывать в вызове не только метод, но и сервис. Поэтому, такой json-rpc можно было бы назвать json-rspc (s-service).
Если бы на него существовала спецификация, то она была бы похожа на спецификацию json-rpc 2.0, за исключением того, что в объекте запроса было бы добавлено поле «service», а поле «id» — было бы не обязательным, и в ответе — необязателен errorCode.
Для демонстрации, я написал простое демо-приложение, в котором реализуются функциональности регистрации, логина, и изменения данных и прав пользователя.
Клиентская часть
Клиентская часть этого приложения — написана на фреймворке AngularJS.
предупреждение
(Считаю своим долгом — предупредить тех, кто ещё не пробовал писать на нём:
{{user.name}}, Ангуляр — тяжёлый наркотик!
Для попадения в зависимость от него — достатчно словить кайф всего один раз.)
{{user.name}}, Ангуляр — тяжёлый наркотик!
Для попадения в зависимость от него — достатчно словить кайф всего один раз.)
Для оформления используется Bootstrap.
В серверной части — Spring.
В качестве реализации объекта json, используется
JSONObject
из библиотеки json-lib.Клиентская часть состоит из трёх файлов:
ajax-connector.js.
Реализация механизма запросов к серверу, инкапсулированная в объекте
Server
.(Префикс ajax — используется, чтобы отличать его от вебсокетного ws-connector.js, которым он может быть заменён, без изменения кода user-controller.js.)
user-controller.js
Здесь находится бизнес-логика приложения, инкапсулированная в функции
userController
.application.html
Графический интерфейс приложения с логикой блокировки элементов.
Как видим, в представлении скриптового кода, удалённый сервер — выглядит как объект Server, который должен быть проинициализирован url'ом.
Через этот объект, мы можем обращаться к любому компоненту на сервере и вызывать любые его методы, таким способом:
Server.call(serviceName, mathodName, [param1, param2, ...], successCallBack, errorCallback, control);
Ответы или ошибки — приходят в соответствующие коллбэки.
Добавление нового сервиса или метода на сервере — никак не затрагивает клиентский код, и мы можем вызывать эти сервисы и методы сразу, после того как они появились в серверном коде.
Естественно, сказав «любому и любые» — я немного отошёл от истины.
На самом деле, как удалённые сервисы, вызываться могут только классы, производные от
AbstractService
, а вызываемые удалённо методы, должны быть аннотированы @Remote
. Для ограничения прав доступа к методам — используется аннотация
@Secured(roleName)
.Так, например, метод, аннотированный
@Secured("Admin")
— не может быть вызван пользователем с ролью «User». Cерверная часть
Весь серверный «фреймворк», если можно так выразиться, занимает меньше 9 кб., и состоит из шести классов, два из которых — уже знакомые нам аннотации: Remote и Secured, а также AbstractService —
абстрактный класс, от которого должны наследоваться все сервисы, и CommonServiceController
В его метод
processAjaxRequest
приходят запросы из скриптового объекта Service
. Далее, находится компонент, по имени сервиса, и на нём, после проверки прав доступа, рефлективно, вызвается указанный метод.
User (entity), для хранения данных о пользователе, и UserManager, для операций с объектом
User
(тестовая реализация с эмуляцией персистентности). Бизнес-логика реализована в двух сервисах: TestUserService — сервис с методами для регистрации, логина, и редактирования данных, и TestAdminService — сервис с методами для удаления юзера, и изменения его роли.
Код написан максимально self-explanatory, поэтому надеюсь, что разобраться в нём будет легко.
Код демо-приложения на Гитхабе.
Что дальше?
В следующей статье, я планирую написать, как, на базе данного подхода, можно организовать клиент-серверное взаимодействие через вебсокеты, и как, на сервере, из вебсокетного контекста, достать сессию http.
Update2:
Update1 — перемещён в тело статьи.