Encodera

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

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

Оглавление

Коды ответа и ошибки

Код ответа — первое, что видит клиент: 200 — всё хорошо, 404 — такого нет, 500 — сломался сервер. Хороший API отвечает правильным кодом и понятным текстом ошибки. В Javalin для этого есть три инструмента: метод ctx.status, готовые исключения-ответы и обработчики исключений в настройках.

Коды ответа

Код задаётся числом или константой из перечисления HttpStatus (пакет io.javalin.http):

// src/main/java/com/example/blog/Main.java
ctx.status(201);
ctx.status(HttpStatus.CREATED);   // то же самое, но читается лучше
КодКонстантаКогда
200OKОбычный успешный ответ, ставится сам
201CREATEDСоздан новый объект
204NO_CONTENTУспех без тела, например после удаления
400BAD_REQUESTКлиент прислал неверные данные
401 / 403UNAUTHORIZED / FORBIDDENНет входа / нет прав
404NOT_FOUNDОбъекта нет
500INTERNAL_SERVER_ERRORОшибка на сервере

Готовые исключения

Чтобы прервать обработчик с ошибкой, бросьте исключение-ответ: NotFoundResponse, BadRequestResponse, ForbiddenResponse и другие. Добавим удаление поста:

// src/main/java/com/example/blog/Main.java
config.routes.delete("/posts/{id}", ctx -> {
    long id = ctx.pathParamAsClass("id", Long.class).get();
    if (!store.delete(id)) {
        throw new NotFoundResponse("Пост " + id + " не найден");
    }
    ctx.status(HttpStatus.NO_CONTENT);
});
curl -i -X DELETE http://localhost:8080/posts/2
curl -i -X DELETE http://localhost:8080/posts/2

Первый запрос вернёт 204 No Content, второй — 404 Not Found с текстом Пост 2 не найден. Если клиент просит JSON заголовком Accept: application/json, Javalin отвечает объектом:

{
    "title": "Пост 2 не найден",
    "status": 404,
    "type": "https://javalin.io/documentation#notfoundresponse",
    "details": {}
}

Свои обработчики исключений

Любое другое исключение Javalin превращает в 500 Server Error. Чтобы ответить по-своему, зарегистрируйте обработчик config.routes.exception. Добавьте в класс Main логгер и обработчик:

// src/main/java/com/example/blog/Main.java
private static final Logger log = LoggerFactory.getLogger(Main.class);   // org.slf4j
 
// внутри Javalin.create(config -> { ... })
config.routes.exception(RuntimeException.class, (e, ctx) -> {
    log.error("Ошибка запроса: {}", ctx.path(), e);
    ctx.status(500).json(Map.of("error", "Что-то пошло не так"));
});

Обработчик ловит RuntimeException и его наследников, кроме исключений-ответов вроде NotFoundResponse: те по-прежнему дают свой код. Проверим временным маршрутом:

// src/main/java/com/example/blog/Main.java
config.routes.get("/boom", ctx -> {
    throw new IllegalStateException("База данных недоступна");
});
curl -i http://localhost:8080/boom
HTTP/1.1 500 Server Error
Content-Type: application/json

{"error":"Что-то пошло не так"}

В логе сервера при этом видно причину: ERROR com.example.blog.Main - Ошибка запроса: /boom и стек java.lang.IllegalStateException.

Обработчики по коду

config.routes.error(404, ...) срабатывает для любого ответа с кодом 404:

// src/main/java/com/example/blog/Main.java
config.routes.error(404, ctx -> ctx.json(Map.of("error", "Не найдено: " + ctx.path())));

Запрос curl http://localhost:8080/nothing вернёт {"error":"Не найдено: /nothing"}. Учтите: этот обработчик заменяет и текст из NotFoundResponse, поэтому в проекте мы его не оставим.

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

Обработчик исключений без записи в лог прячет ошибку. Клиент видит {"error":"Что-то пошло не так"}, а в консоли сервера пусто — найти причину невозможно. Всегда передавайте исключение в логгер последним аргументом, как log.error(..., e) выше: тогда SLF4J напечатает полный стек.

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

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