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

Создание микросервисов REST с помощью Javalin

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

1. Введение

Javalin — это легкий веб-фреймворк, написанный для Java и Kotlin. Он написан поверх веб-сервера Jetty, что делает его высокопроизводительным. Javalin смоделирован близко к koa.js , что означает, что он написан с нуля, чтобы его было легко понять и использовать.

В этом руководстве мы рассмотрим этапы создания базовой микрослужбы REST с использованием этой облегченной платформы.

2. Добавление зависимостей

Для создания базового приложения нам нужна только одна зависимость — сам Javalin:

<dependency>
<groupId>io.javalin</groupId>
<artifactId>javalin</artifactId>
<version>1.6.1</version>
</dependency>

Актуальную версию можно найти здесь .

3. Настройка Джавалина

Javalin упрощает настройку базового приложения. Мы собираемся начать с определения нашего основного класса и настройки простого приложения «Hello World».

Давайте создадим новый файл в нашем базовом пакете с именем JavalinApp.java .

Внутри этого файла мы создаем основной метод и добавляем следующее для настройки базового приложения:

Javalin app = Javalin.create()
.port(7000)
.start();
app.get("/hello", ctx -> ctx.html("Hello, Javalin!"));

Мы создаем новый экземпляр Javalin, заставляем его прослушивать порт 7000, а затем запускаем приложение.

Мы также настраиваем нашу первую конечную точку, прослушивающую запрос GET в конечной точке / hello .

Давайте запустим это приложение и посетим http://localhost:7000/hello , чтобы увидеть результаты.

4. Создание пользовательского контроллера

Пример «Hello World» отлично подходит для ознакомления с темой, но бесполезен для реального приложения. Теперь давайте рассмотрим более реалистичный вариант использования Javalin.

Во-первых, нам нужно создать модель объекта, с которым мы работаем. Начнем с создания пакета с именем user в корневом проекте.

Затем мы добавляем новый класс User :

public class User {
public final int id;
public final String name;

// constructors
}

Кроме того, нам нужно настроить наш объект доступа к данным (DAO). В этом примере мы будем использовать объект в памяти для хранения наших пользователей.

Мы создаем новый класс в пользовательском пакете с именем UserDao.java:

class UserDao {

private List<User> users = Arrays.asList(
new User(0, "Steve Rogers"),
new User(1, "Tony Stark"),
new User(2, "Carol Danvers")
);

private static UserDao userDao = null;

private UserDao() {
}

static UserDao instance() {
if (userDao == null) {
userDao = new UserDao();
}
return userDao;
}

Optional<User> getUserById(int id) {
return users.stream()
.filter(u -> u.id == id)
.findAny();
}

Iterable<String> getAllUsernames() {
return users.stream()
.map(user -> user.name)
.collect(Collectors.toList());
}
}

Реализация нашего DAO в виде синглтона упрощает его использование в примере. Мы также могли бы объявить его как статический член нашего основного класса или использовать внедрение зависимостей из библиотеки, такой как Guice, если бы захотели.

Наконец, мы хотим создать наш класс контроллера. Javalin позволяет нам быть очень гибкими при объявлении наших обработчиков маршрутов, так что это только один из способов их определения.

Мы создаем новый класс с именем UserController.java в пользовательском пакете:

public class UserController {
public static Handler fetchAllUsernames = ctx -> {
UserDao dao = UserDao.instance();
Iterable<String> allUsers = dao.getAllUsernames();
ctx.json(allUsers);
};

public static Handler fetchById = ctx -> {
int id = Integer.parseInt(Objects.requireNonNull(ctx.param("id")));
UserDao dao = UserDao.instance();
User user = dao.getUserById(id);
if (user == null) {
ctx.html("Not Found");
} else {
ctx.json(user);
}
};
}

Объявляя обработчики как статические, мы гарантируем, что сам контроллер не хранит никакого состояния. Но в более сложных приложениях мы можем захотеть сохранять состояние между запросами, и в этом случае нам нужно будет удалить модификатор static.

Также обратите внимание, что модульное тестирование сложнее со статическими методами, поэтому, если мы хотим такого уровня тестирования, нам нужно будет использовать нестатические методы.

5. Добавление маршрутов

Теперь у нас есть несколько способов извлечения данных из нашей модели. Последний шаг — предоставить эти данные через конечные точки REST. Нам нужно зарегистрировать два новых маршрута в нашем основном приложении.

Давайте добавим их в наш основной класс приложения:

app.get("/users", UserController.fetchAllUsernames);
app.get("/users/:id", UserController.fetchById);

После компиляции и запуска приложения мы можем сделать запрос к каждой из этих новых конечных точек. При вызове http: //localhost:7000/users будут перечислены все пользователи, а при вызове http://localhost:7000/users/0 будет получен единственный объект User JSON с идентификатором 0. Теперь у нас есть микросервис, который позволяет нам получать данные о пользователях . данные.

6. Расширение маршрутов

Получение данных — жизненно важная задача большинства микросервисов.

Однако нам также необходимо иметь возможность хранить данные в нашем хранилище данных. Javalin предоставляет полный набор обработчиков путей, необходимых для создания сервисов.

Мы видели пример GET выше, но также возможны PATCH, POST, DELETE и PUT .

Кроме того, если мы включим Jackson в качестве зависимости, мы сможем автоматически анализировать тела запросов JSON в наших классах моделей. Например:

app.post("/") { ctx ->
User user = ctx.bodyAsClass(User.class);
}

позволит нам получить объект пользователя JSON из тела запроса и преобразовать его в объект модели пользователя .

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

Мы можем объединить все эти методы, чтобы сделать наш микросервис.

В этой статье мы увидели, как настроить Javalin и создать простое приложение. Мы также говорили о том, как использовать различные типы методов HTTP, чтобы клиенты могли взаимодействовать с нашим сервисом.

Для более продвинутых примеров использования Javalin обязательно ознакомьтесь с документацией .

Также, как всегда, код можно найти на GitHub .