Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

Контроллеры и apiBuilder

Когда все обработчики записаны лямбдами в main, метод быстро разрастается до сотни строк. Принято выносить обработчики в отдельный класс — контроллер. Каждый его метод принимает Context и подходит в качестве обработчика. Маршруты при этом остаются в Main, а для их группировки в Javalin есть apiBuilder — набор статических методов path, get, post, delete.

Класс-контроллер

Создайте файл контроллера постов. Хранилище он получает через конструктор:

// src/main/java/com/example/blog/PostController.java
package com.example.blog;
 
import io.javalin.http.Context;
import io.javalin.http.HttpStatus;
import io.javalin.http.NotFoundResponse;
 
public class PostController {
    private final PostStore store;
 
    public PostController(PostStore store) {
        this.store = store;
    }
 
    public void getAll(Context ctx) {
        ctx.json(store.findAll());
    }
 
    public void getOne(Context ctx) {
        long id = ctx.pathParamAsClass("id", Long.class).get();
        Post post = store.findById(id)
                .orElseThrow(() -> new NotFoundResponse("Пост " + id + " не найден"));
        ctx.json(post);
    }
 
    public void create(Context ctx) {
        NewPost data = ctx.bodyValidator(NewPost.class)
                .check(p -> p.title() != null && !p.title().isBlank(), "Нужен заголовок")
                .check(p -> p.body() != null && p.body().length() >= 5, "Текст слишком короткий")
                .get();
        ctx.status(HttpStatus.CREATED).json(store.create(data));
    }
 
    public void delete(Context ctx) {
        long id = ctx.pathParamAsClass("id", Long.class).get();
        if (!store.delete(id)) {
            throw new NotFoundResponse("Пост " + id + " не найден");
        }
        ctx.status(HttpStatus.NO_CONTENT);
    }
}

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

Маршруты через apiBuilder

В Main.java создайте контроллер и замените маршруты постов блоком apiBuilder:

// src/main/java/com/example/blog/Main.java
package com.example.blog;
 
import io.javalin.Javalin;
 
import static io.javalin.apibuilder.ApiBuilder.delete;
import static io.javalin.apibuilder.ApiBuilder.get;
import static io.javalin.apibuilder.ApiBuilder.path;
import static io.javalin.apibuilder.ApiBuilder.post;
 
public class Main {
    public static void main(String[] args) {
        var posts = new PostController(new PostStore());
 
        Javalin app = Javalin.create(config -> {
            config.routes.apiBuilder(() -> {
                path("/posts", () -> {
                    get(posts::getAll);
                    post(posts::create);
                    path("/{id}", () -> {
                        get(posts::getOne);
                        delete(posts::delete);
                    });
                });
            });
            // обработчики before, after и exception из прошлых уроков остаются здесь
        });
        app.start(8080);
    }
}

path задаёт общую часть адреса, вложенные path её продолжают. Запись posts::getAll — ссылка на метод конкретного объекта posts. По структуре блока сразу видно все адреса API.

Вызов внутри блокаИтоговый маршрут
get(posts::getAll) в path("/posts")GET /posts
post(posts::create) в path("/posts")POST /posts
get(posts::getOne) в path("/{id}")GET /posts/{id}
delete(posts::delete) в path("/{id}")DELETE /posts/{id}

API работает как раньше:

curl -X POST http://localhost:8080/posts \
     -d '{"title":"Контроллеры","body":"Код переехал в класс.","author":"Олег"}'
{"id":3,"title":"Контроллеры","body":"Код переехал в класс.","author":"Олег"}

Частая ошибка

Статические методы get, post, path из ApiBuilder работают только внутри config.routes.apiBuilder(...). Если вызвать get("/", ...) прямо в блоке настроек, сервер падает при старте:

Exception in thread "main" java.lang.IllegalStateException: The static API can only be used within a routes() call.

Внутри Javalin.create пишите config.routes.get(...), а короткие get(...) — только внутри apiBuilder.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.