Перейти к основному содержимому

Создание API с помощью Spark Java Framework

· 8 мин. чтения

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. Согласно документации , каждый маршрут состоит из трех простых частей — глагола , пути и обратного вызова .

  1. Глагол — это метод, соответствующий методу HTTP . Глагольные методы включают в себя: получить, опубликовать, поставить, удалить, возглавить, отследить, подключить и параметры .
  2. Путь (также называемый шаблоном маршрута) определяет, какие URI маршрут должен прослушивать и предоставлять ответ для
  3. Обратный вызов — это функция-обработчик, которая вызывается для данного глагола и пути, чтобы сгенерировать и вернуть ответ на соответствующий 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 .