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

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

1. Вступление

В этой статье у нас будет краткое введение вSpark framework. 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:


    com.sparkjava
    spark-core
    2.5.4

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

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

В различных местах в примере мы будем использовать библиотеку Gson для операций JSON. Чтобы включить Gson в свой проект, включите эту зависимость в свойpom.xml:


    com.google.code.gson
    gson
    2.8.0

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

3. Начало работы с Spark Framework

Давайте посмотрим на основные строительные блоки приложения Spark и продемонстрируем быстрый веб-сервис.

3.1. Маршруты

Веб-сервисы в Spark Java построены на маршрутах и ​​их обработчиках. Маршруты являются важными элементами в Spark. Согласноdocumentation, каждый маршрут состоит из трех простых частей - averb, apath и acallback.

  1. verb - это метод, соответствующий методу HTTP. Глагольные методы включают:get, post, put, delete, head, trace, connect, иoptions

  2. path (также называемый шаблоном маршрута) определяет, какие URI следует прослушивать, и предоставлять ответ для

  3. callback - это функция-обработчик, которая вызывается для данного глагола и пути, чтобы сгенерировать и вернуть ответ на соответствующий 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 - этоlambda expression, придающий этой структуре функциональное программирование.

Лямбда-выражение имеет запрос и ответ в качестве аргументов и помогает вернуть ответ. Мы поместим логику нашего контроллера в лямбда-выражение для маршрутов REST API, как мы увидим позже в этом руководстве.

3.3. Тестирование Hello World API

После запуска классаHelloWorldService как обычного класса Java вы сможете получить доступ к службе через порт по умолчанию4567, используя маршруты, определенные с помощью методаget выше.

Давайте посмотрим на запрос и ответ для первого маршрута:

Запрос:

GET http://localhost:4567/hello

Отклик:

Hello, world

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

Запрос:

GET http://localhost:4567/hello/example

Отклик:

Hello, example

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

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

В этом разделе мы разработаем простой веб-сервис REST для следующей сущностиUser:

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 - получить пользователя с заданным идентификатором

  • POST / users /: id - добавить пользователя

  • PUT / users /: id - редактировать конкретного пользователя

  • ОПЦИИ / users /: id - проверить, существует ли пользователь с данным идентификатором

  • DELETE / users /: id - удалить конкретного пользователя

4.2. Пользовательский сервис

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

public interface UserService {

    public void addUser (User user);

    public Collection 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 для имитации персистентности. You can supply your own implementation with the database and persistence layer of your choice.

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

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

{
    status: 
    message: 
    data: 
}

Значение поляstatus может быть либоSUCCESS, либоERROR. Полеdata будет содержать JSON-представление возвращаемых данных, напримерUser или коллекциюUsers.

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

Давайте представим указанную выше структуру 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 - этоenum, определенный ниже:

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, который добавитUser:

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

Note: В этом примере JSON-представление объектаUser передается как необработанное тело запроса POST.

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

Запрос:

POST http://localhost:4567/users
{
    "id": "1012",
    "email": "[email protected]",
    "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":"[email protected]"
        },
        {
            "id":"1012",
            "firstName":"Mac",
            "lastName":"Mason1",
            "email":"[email protected]"
        }
    ]
}

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

Ниже приведен обработчик ответа методаget, который возвращаетUser с заданнымid:

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":"[email protected]"
    }
}

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

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

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

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

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

Запрос:

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

Отклик:

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

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

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

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, который проверяет, существует лиUser с заданнымid:

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 project.