1. Введение
В этой статье мы кратко познакомимся с фреймворком Spark . Spark framework — это веб-фреймворк для быстрой разработки, вдохновленный фреймворком Sinatra для Ruby и построенный на философии Java 8 Lambda Expression, что делает его менее подробным, чем большинство приложений, написанных в других фреймворках Java.
Это хороший выбор, если вы хотите получить опыт работы с Node.js
при разработке веб-API или микросервисов на Java. С помощью Spark вы можете получить готовый REST API для обслуживания JSON менее чем за десять строк кода.
Мы быстро начнем с примера «Hello World», за которым последует простой REST API.
2. Зависимости Maven
2.1. Искра Фреймворк
Включите следующую зависимость Maven в ваш pom.xml
:
<dependency>
<groupId>com.sparkjava</groupId>
<artifactId>spark-core</artifactId>
<version>2.5.4</version>
</dependency>
Вы можете найти последнюю версию Spark на Maven Central .
2.2. Библиотека Гсона
В разных местах примера мы будем использовать библиотеку Gson для операций JSON. Чтобы включить Gson в свой проект, включите эту зависимость в свой pom.xml
:
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.0</version>
</dependency>
Вы можете найти последнюю версию Gson на Maven Central .
3. Начало работы со Spark Framework
Давайте рассмотрим основные строительные блоки приложения Spark и продемонстрируем быстрый веб-сервис.
3.1. Маршруты
Веб-сервисы в Spark Java строятся на маршрутах и их обработчиках. Маршруты являются важными элементами Spark. Согласно документации , каждый маршрут состоит из трех простых частей — глагола
, пути
и обратного вызова
.
- Глагол — это метод, соответствующий методу HTTP . Глагольные методы включают в себя:
получить, опубликовать, поставить, удалить, возглавить, отследить, подключить
ипараметры .
- Путь (также называемый шаблоном маршрута) определяет, какие URI маршрут должен прослушивать и предоставлять ответ для
- Обратный вызов — это функция-обработчик, которая вызывается для данного глагола и пути, чтобы сгенерировать и вернуть ответ на соответствующий HTTP-запрос. Обратный вызов принимает объект запроса и объект ответа в качестве аргументов.
Здесь мы показываем базовую структуру маршрута, использующего глагол get :
get("/your-route-path/", (request, response) -> {
// your callback code
});
3.2. Привет, мир API
Давайте создадим простой веб-сервис, который имеет два маршрута для GET-запросов и возвращает в ответ сообщения «Hello». Эти маршруты используют метод get
, который является статическим импортом из класса spark.Spark
:
import static spark.Spark.*;
public class HelloWorldService {
public static void main(String[] args) {
get("/hello", (req, res)->"Hello, world");
get("/hello/:name", (req,res)->{
return "Hello, "+ req.params(":name");
});
}
}
Первый аргумент метода get
— это путь к маршруту. Первый маршрут содержит статический путь, представляющий только один URI ( «/hello»
).
Путь второго маршрута ( «/hello/:name»
) содержит заполнитель для параметра «name»
, что обозначается двоеточием («:») перед параметром. Этот маршрут будет вызываться в ответ на запросы GET к URI, таким как «/hello/Joe»
и «/hello/Mary»
.
Второй аргумент метода get — это
лямбда-выражение , придающее фреймворку оттенок функционального программирования.
Лямбда-выражение имеет запрос и ответ в качестве аргументов и помогает вернуть ответ. Мы поместим логику нашего контроллера в лямбда-выражение для маршрутов REST API, как мы увидим позже в этом руководстве.
3.3. Тестирование Hello World API
После запуска класса HelloWorldService
как обычного класса Java вы сможете получить доступ к сервису через его порт по умолчанию 4567
, используя маршруты, определенные с помощью метода get выше.
Давайте посмотрим на запрос и ответ для первого маршрута:
Запрос:
GET http://localhost:4567/hello
Ответ:
Hello, world
Давайте протестируем второй маршрут, передав параметр name
в своем пути:
Запрос:
GET http://localhost:4567/hello/foreach
Ответ:
Hello, foreach
Посмотрите, как размещение текста «foreach»
в URI использовалось для сопоставления с шаблоном маршрута «/hello/:name»
, что привело к вызову функции обработчика обратного вызова второго маршрута.
4. Проектирование службы RESTful
В этом разделе мы разработаем простую веб-службу REST для следующего объекта пользователя :
public class User {
private String id;
private String firstName;
private String lastName;
private String email;
// constructors, getters and setters
}
4.1. Маршруты
Давайте перечислим маршруты, из которых состоит наш API:
- GET /users — получить список всех пользователей
- GET /users/:id — получить пользователя с заданным id
- POST /users/:id — добавить пользователя
- PUT /users/:id — редактировать конкретного пользователя
- ВАРИАНТЫ /users/:id — проверить, существует ли пользователь с данным id
- DELETE /users/:id — удалить определенного пользователя
4.2. Пользовательская служба
Ниже представлен интерфейс UserService
, объявляющий операции CRUD для объекта User
:
public interface UserService {
public void addUser (User user);
public Collection<User> getUsers ();
public User getUser (String id);
public User editUser (User user)
throws UserException;
public void deleteUser (String id);
public boolean userExist (String id);
}
В демонстрационных целях мы предоставляем реализацию Map
этого интерфейса UserService
в коде GitHub для имитации постоянства. Вы можете предоставить свою собственную реализацию с базой данных и уровнем сохраняемости по вашему выбору.
4.3. Структура ответа JSON
Ниже представлена структура JSON ответов, используемых в нашем сервисе REST:
{
status: <STATUS>
message: <TEXT-MESSAGE>
data: <JSON-OBJECT>
}
Значение поля статуса
может быть либо SUCCESS
, либо ERROR
. Поле данных
будет содержать JSON-представление возвращаемых данных, например User
или collection of Users
.
Если данные не возвращаются или состояние
равно ERROR
, мы заполним поле сообщения
, чтобы указать причину ошибки или отсутствия возвращаемых данных.
Давайте представим приведенную выше структуру JSON с помощью класса Java:
public class StandardResponse {
private StatusResponse status;
private String message;
private JsonElement data;
public StandardResponse(StatusResponse status) {
// ...
}
public StandardResponse(StatusResponse status, String message) {
// ...
}
public StandardResponse(StatusResponse status, JsonElement data) {
// ...
}
// getters and setters
}
где StatusResponse
— это перечисление
, определенное следующим образом:
public enum StatusResponse {
SUCCESS ("Success"),
ERROR ("Error");
private String status;
// constructors, getters
}
5. Внедрение RESTful-сервисов
Теперь давайте реализуем маршруты и обработчики для нашего REST API.
5.1. Создание контроллеров
Следующий класс Java содержит маршруты для нашего API, включая глаголы и пути, а также схему обработчиков для каждого маршрута:
public class SparkRestExample {
public static void main(String[] args) {
post("/users", (request, response) -> {
//...
});
get("/users", (request, response) -> {
//...
});
get("/users/:id", (request, response) -> {
//...
});
put("/users/:id", (request, response) -> {
//...
});
delete("/users/:id", (request, response) -> {
//...
});
options("/users/:id", (request, response) -> {
//...
});
}
}
Мы покажем полную реализацию каждого обработчика маршрута в следующих подразделах.
5.2. Добавить пользователя
Ниже приведен обработчик ответа метода post
, который добавит пользователя
:
post("/users", (request, response) -> {
response.type("application/json");
User user = new Gson().fromJson(request.body(), User.class);
userService.addUser(user);
return new Gson()
.toJson(new StandardResponse(StatusResponse.SUCCESS));
});
Примечание. В этом примере JSON-представление объекта User
передается как необработанное тело запроса POST.
Проверим маршрут:
Запрос:
POST http://localhost:4567/users
{
"id": "1012",
"email": "your-email@your-domain.com",
"firstName": "Mac",
"lastName": "Mason1"
}
Ответ:
{
"status":"SUCCESS"
}
5.3. Получить всех пользователей
Ниже показан обработчик ответа метода get
, который возвращает всех пользователей из UserService
:
get("/users", (request, response) -> {
response.type("application/json");
return new Gson().toJson(
new StandardResponse(StatusResponse.SUCCESS,new Gson()
.toJsonTree(userService.getUsers())));
});
Теперь давайте проверим маршрут:
Запрос:
GET http://localhost:4567/users
Ответ:
{
"status":"SUCCESS",
"data":[
{
"id":"1014",
"firstName":"John",
"lastName":"Miller",
"email":"your-email@your-domain.com"
},
{
"id":"1012",
"firstName":"Mac",
"lastName":"Mason1",
"email":"your-email@your-domain.com"
}
]
}
5.4. Получить пользователя по идентификатору
Ниже приведен обработчик ответа метода get , который возвращает
пользователя
с заданным идентификатором
:
get("/users/:id", (request, response) -> {
response.type("application/json");
return new Gson().toJson(
new StandardResponse(StatusResponse.SUCCESS,new Gson()
.toJsonTree(userService.getUser(request.params(":id")))));
});
Теперь давайте проверим маршрут:
Запрос:
GET http://localhost:4567/users/1012
Ответ:
{
"status":"SUCCESS",
"data":{
"id":"1012",
"firstName":"Mac",
"lastName":"Mason1",
"email":"your-email@your-domain.com"
}
}
5.5. Изменить пользователя
Ниже приведен обработчик ответа метода put , который редактирует пользователя, имеющего
идентификатор
, указанный в шаблоне маршрута:
put("/users/:id", (request, response) -> {
response.type("application/json");
User toEdit = new Gson().fromJson(request.body(), User.class);
User editedUser = userService.editUser(toEdit);
if (editedUser != null) {
return new Gson().toJson(
new StandardResponse(StatusResponse.SUCCESS,new Gson()
.toJsonTree(editedUser)));
} else {
return new Gson().toJson(
new StandardResponse(StatusResponse.ERROR,new Gson()
.toJson("User not found or error in edit")));
}
});
Примечание. В этом примере данные передаются в необработанном теле запроса POST в виде объекта JSON, имена свойств которого соответствуют полям редактируемого объекта « Пользователь ».
Проверим маршрут:
Запрос:
PUT http://localhost:4567/users/1012
{
"lastName": "Mason"
}
Ответ:
{
"status":"SUCCESS",
"data":{
"id":"1012",
"firstName":"Mac",
"lastName":"Mason",
"email":"your-email@your-domain.com"
}
}
5.6. Удалить пользователя
Ниже приведен обработчик ответа метода удаления
, который удалит пользователя
с заданным идентификатором
:
delete("/users/:id", (request, response) -> {
response.type("application/json");
userService.deleteUser(request.params(":id"));
return new Gson().toJson(
new StandardResponse(StatusResponse.SUCCESS, "user deleted"));
});
Теперь давайте проверим маршрут:
Запрос:
DELETE http://localhost:4567/users/1012
Ответ:
{
"status":"SUCCESS",
"message":"user deleted"
}
5.7. Проверить, существует ли пользователь
Метод options
— хороший выбор для условной проверки. Ниже приведен обработчик ответа метода options
, который проверит, существует ли пользователь
с данным идентификатором
:
options("/users/:id", (request, response) -> {
response.type("application/json");
return new Gson().toJson(
new StandardResponse(StatusResponse.SUCCESS,
(userService.userExist(
request.params(":id"))) ? "User exists" : "User does not exists" ));
});
Теперь давайте проверим маршрут:
Запрос:
OPTIONS http://localhost:4567/users/1012
Ответ:
{
"status":"SUCCESS",
"message":"User exists"
}
6. Заключение
В этой статье мы кратко познакомились с фреймворком Spark для быстрой веб-разработки.
Этот фреймворк в основном продвигается для создания микросервисов на Java. Разработчики Node.js
со знанием Java, которые хотят использовать библиотеки, созданные на основе библиотек JVM, должны чувствовать себя как дома, используя эту платформу.
И как всегда, все исходники для этого туториала вы можете найти в проекте Github .