«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. Spark Framework

Включите в pom.xml следующую зависимость Maven:

<dependency>
    <groupId>com.sparkjava</groupId>
    <artifactId>spark-core</artifactId>
    <version>2.5.4</version>
</dependency>

Вы можете найти последнюю версию Spark на Maven Central.

2.2. Библиотека Gson

В разных местах примера мы будем использовать библиотеку 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. The verb is a method corresponding to an HTTP method. Verb methods include: get, post, put, delete, head, trace, connect, and options
  2. The path (also called a route pattern) determines which URI(s) the route should listen to and provide a response for
  3. The callback is a handler function that is invoked for a given verb and path in order to generate and return a response to the corresponding HTTP request. A callback takes a request object and response object as arguments

Здесь мы показываем базовую структуру маршрута, использующего глагол get:

get("/your-route-path/", (request, response) -> {
    // your callback code
});

3.2. Hello World 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

Давайте проверим второй маршрут, передав в своем пути параметр имени: ~~ ~ Запрос:

Ответ:

GET http://localhost:4567/hello/baeldung

Посмотрите, как размещение текста «baeldung» в URI использовалось для сопоставления с шаблоном маршрута «/hello/:name» — вызывает вызов функции обработчика обратного вызова второго маршрута.

Hello, baeldung

4. Проектирование службы RESTful

В этом разделе мы разработаем простую веб-службу REST для следующего объекта пользователя:

4.1. Маршруты

public class User {
    private String id;
    private String firstName;
    private String lastName;
    private String email;

    // constructors, getters and setters
}

Давайте перечислим маршруты, составляющие наш API:

GET /users — получить список всех пользователей GET /users/:id — получить пользователя с заданным идентификатором POST /users/:id — » добавить пользователя PUT /users/:id — изменить определенного пользователя OPTIONS /users/:id — проверить, существует ли пользователь с данным идентификатором DELETE /users/:id — удалить определенного пользователя

    4.2. Пользовательская служба

Ниже приведен интерфейс UserService, объявляющий операции CRUD для сущности User:

В демонстрационных целях мы предоставляем реализацию Map этого интерфейса UserService в коде GitHub для имитации постоянства. Вы можете предоставить свою собственную реализацию с базой данных и уровнем сохраняемости по вашему выбору.

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);
}

4.3. Структура ответа JSON

«Ниже представлена ​​структура JSON ответов, используемых в нашей службе REST:

Значением поля статуса может быть либо УСПЕХ, либо ОШИБКА. Поле данных будет содержать JSON-представление возвращаемых данных, например, пользователя или набор пользователей.

{
    status: <STATUS>
    message: <TEXT-MESSAGE>
    data: <JSON-OBJECT>
}

Если данные не возвращаются или состояние равно ERROR, мы заполним поле сообщения, чтобы указать причину ошибки или отсутствия возвращаемых данных.

Давайте представим приведенную выше структуру JSON с помощью класса Java:

где StatusResponse — это перечисление, определенное следующим образом:

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
}

5. Реализация служб RESTful

public enum StatusResponse {
    SUCCESS ("Success"),
    ERROR ("Error");
 
    private String status;       
    // constructors, getters
}

Теперь давайте реализуем маршруты и обработчики. для нашего 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, который добавит пользователя:

Примечание. В этом примере JSON-представление объекта User передается как необработанное тело запроса 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));
});

Протестируем маршрут:

Запрос:

Ответ:

POST http://localhost:4567/users
{
    "id": "1012", 
    "email": "[email protected]", 
    "firstName": "Mac",
    "lastName": "Mason1"
}

5.3. Получить всех пользователей

{
    "status":"SUCCESS"
}

Ниже приведен обработчик ответа метода 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

~ ~~ 5.4. Получить пользователя по идентификатору

{
    "status":"SUCCESS",
    "data":[
        {
            "id":"1014",
            "firstName":"John",
            "lastName":"Miller",
            "email":"[email protected]"
        },
        {
            "id":"1012",
            "firstName":"Mac",
            "lastName":"Mason1",
            "email":"[email protected]"
        }
    ]
}

Ниже приведен обработчик ответа метода 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

5.5. Редактирование пользователя

{
    "status":"SUCCESS",
    "data":{
        "id":"1012",
        "firstName":"Mac",
        "lastName":"Mason1",
        "email":"[email protected]"
    }
}

Ниже приведен обработчик ответа метода put, который редактирует пользователя, имеющего идентификатор, указанный в шаблоне маршрута:

Примечание. В этом примере данные передаются в необработанном теле POST. request как объект JSON, имена свойств которого соответствуют полям редактируемого объекта User.

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")));
    }
});

Протестируем маршрут:

Запрос:

Ответ:

PUT http://localhost:4567/users/1012
{
    "lastName": "Mason"
}

5.6. Удаление пользователя

{
    "status":"SUCCESS",
    "data":{
        "id":"1012",
        "firstName":"Mac",
        "lastName":"Mason",
        "email":"[email protected]"
    }
}

Ниже приведен обработчик ответа метода удаления, который удалит пользователя с заданным идентификатором:

Теперь давайте проверим маршрут:

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

5.7. Проверить, существует ли пользователь

{
    "status":"SUCCESS",
    "message":"user deleted"
}

Метод 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

6. Заключение

{
    "status":"SUCCESS",
    "message":"User exists"
}

В этой статье мы познакомились с фреймворком Spark для быстрой веб-разработки.

Этот фреймворк в основном продвигается для создания микросервисов на Java. Разработчики Node.js со знанием Java, которые хотят использовать библиотеки, созданные на основе библиотек JVM, должны чувствовать себя как дома, используя эту платформу.

И, как всегда, вы можете найти все исходники для этого туториала в проекте Github.

«