Создание 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.
-
verb - это метод, соответствующий методу HTTP. Глагольные методы включают:get, post, put, delete, head, trace, connect, иoptions
-
path (также называемый шаблоном маршрута) определяет, какие URI следует прослушивать, и предоставлять ответ для
-
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.