Encodera

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

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

Оглавление

JSON, записи и тело запроса

REST API обычно обменивается данными в формате JSON. Расширение quarkus-rest-jackson превращает Java-объекты в JSON и обратно через библиотеку Jackson. Писать преобразование вручную не нужно: метод возвращает объект, а клиент получает JSON. Для описания данных удобнее всего записи (records) из Java 16+: короткие неизменяемые классы, у которых Jackson видит все поля.

Модель поста

// src/main/java/com/example/blog/Post.java
package com.example.blog;
 
public record Post(long id, String title, String body) {
}

Запись Post описывает пост блога: номер, заголовок и текст.

Список постов в JSON

Пока база данных не подключена, будем хранить посты в памяти — в поле ресурса. Ресурс создаётся один раз, поэтому данные живут, пока работает приложение. Перепишите PostResource так:

// src/main/java/com/example/blog/PostResource.java
package com.example.blog;
 
import jakarta.ws.rs.GET;
import jakarta.ws.rs.NotFoundException;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
 
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
 
@Path("/posts")
public class PostResource {
 
    private final Map<Long, Post> posts = new ConcurrentHashMap<>();
    private final AtomicLong nextId = new AtomicLong(1);
 
    public PostResource() {
        long id = nextId.getAndIncrement();
        posts.put(id, new Post(id, "Привет, Quarkus", "Первый пост блога."));
    }
 
    @GET
    public List<Post> list() {
        return List.copyOf(posts.values());
    }
 
    @GET
    @Path("/{id}")
    public Post get(@PathParam("id") long id) {
        Post post = posts.get(id);
        if (post == null) {
            throw new NotFoundException();
        }
        return post;
    }
}

ConcurrentHashMap и AtomicLong безопасны, когда запросы приходят одновременно. Аннотации @Produces нет — для объектов Quarkus сам отдаёт JSON. Исключение NotFoundException превращается в ответ 404.

curl http://localhost:8080/posts
curl http://localhost:8080/posts/1
[{"id":1,"title":"Привет, Quarkus","body":"Первый пост блога."}]
{"id":1,"title":"Привет, Quarkus","body":"Первый пост блога."}

Приём JSON в POST-запросе

Для данных, которые присылает клиент, создадим отдельную запись — без id, ведь его назначает сервер:

// src/main/java/com/example/blog/NewPost.java
package com.example.blog;
 
public record NewPost(String title, String body) {
}

Параметр метода без аннотации — это тело запроса. Quarkus прочитает JSON и создаст из него NewPost:

// src/main/java/com/example/blog/PostResource.java
@POST
public Response create(NewPost newPost) {
    long id = nextId.getAndIncrement();
    Post post = new Post(id, newPost.title(), newPost.body());
    posts.put(id, post);
    return Response.created(URI.create("/posts/" + id))
            .entity(post)
            .build();
}

Добавьте импорты jakarta.ws.rs.POST, jakarta.ws.rs.core.Response и java.net.URI. Класс Response позволяет задать статус и заголовки: created(...) даёт статус 201 Created и заголовок Location с адресом нового поста, entity(...) — тело ответа.

curl -i -X POST http://localhost:8080/posts \
     -H "Content-Type: application/json" \
     -d '{"title":"Второй пост","body":"Текст"}'
HTTP/1.1 201 Created
Content-Type: application/json;charset=UTF-8
Location: http://localhost:8080/posts/2

{"id":2,"title":"Второй пост","body":"Текст"}

В Windows кириллица в аргументах curl может испортиться, и сервер ответит 400. Тогда сохраните JSON в файл в кодировке UTF-8 и отправьте его так: curl -X POST http://localhost:8080/posts -H "Content-Type: application/json" --data-binary @post.json.

Удаление и ответ без тела

// src/main/java/com/example/blog/PostResource.java
@DELETE
@Path("/{id}")
public Response delete(@PathParam("id") long id) {
    if (posts.remove(id) == null) {
        throw new NotFoundException();
    }
    return Response.noContent().build();
}

curl -i -X DELETE http://localhost:8080/posts/2 вернёт HTTP/1.1 204 No Content — удачный ответ без тела. Импорт — jakarta.ws.rs.DELETE.

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

Если отправить JSON без заголовка Content-Type, сервер его не примет:

curl -i -X POST http://localhost:8080/posts -d '{"title":"x","body":"y"}'
HTTP/1.1 415 Unsupported Media Type
content-length: 0

С флагом -d curl по умолчанию отправляет application/x-www-form-urlencoded, а метод ждёт JSON. Добавьте -H "Content-Type: application/json". Если же JSON сломан, например не хватает скобки, ответ будет 400 Bad Request.

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

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