Обработчики before и after
Иногда один и тот же код нужен для многих маршрутов: записать запрос в лог, добавить заголовок, проверить вход. Копировать его в каждый обработчик неудобно. Для этого в Javalin есть обработчики before и after: первые выполняются до основного обработчика, вторые — после. В других фреймворках такое называют фильтрами или middleware.
Порядок выполнения
Каждый запрос проходит одну и ту же цепочку:
- Все подходящие обработчики
beforeв порядке объявления. - Один основной обработчик маршрута (endpoint).
- Обработчики исключений и ошибок, если что-то пошло не так.
- Все подходящие обработчики
after— они выполняются даже после ошибки.
Лог каждого запроса
Добавьте в блок настроек два обработчика. before запоминает время начала в атрибуте запроса, after печатает метод, путь, код и длительность:
// src/main/java/com/example/blog/Main.java
config.routes.before(ctx -> ctx.attribute("start", System.nanoTime()));
config.routes.after(ctx -> {
long start = ctx.attribute("start");
long ms = (System.nanoTime() - start) / 1_000_000;
System.out.println(ctx.method() + " " + ctx.path() + " -> " + ctx.statusCode() + " (" + ms + " мс)");
});Атрибут — значение, которое живёт только во время одного запроса: ctx.attribute("имя", значение) сохраняет его, ctx.attribute("имя") читает. Вызовите несколько адресов и посмотрите в консоль сервера:
GET /posts -> 200 (5 мс)
GET /posts/99 -> 404 (2 мс)
POST /posts -> 201 (45 мс)
DELETE /posts/3 -> 204 (0 мс)
Первый запрос к JSON всегда медленнее: Jackson при первом вызове готовит свои внутренние структуры.
Обработчики для части адресов
Первым аргументом можно передать путь. Звёздочка * означает «что угодно дальше»:
// src/main/java/com/example/blog/Main.java
config.routes.before("/posts/*", ctx -> System.out.println("Запрос к посту: " + ctx.path()));
config.routes.after(ctx -> ctx.header("X-Blog", "Javalin"));Первый обработчик сработает для /posts/1 и /posts/new, второй добавит заголовок X-Blog: Javalin ко всем ответам, включая ответы 404.
before и beforeMatched
Обработчик before выполняется для любого запроса, даже если маршрута нет и ответ будет 404. Если код нужен только для существующих маршрутов, используйте beforeMatched и afterMatched. В главе 4 на beforeMatched мы построим проверку прав.
| Метод | Когда выполняется |
|---|---|
before, after | Для всех запросов |
beforeMatched, afterMatched | Только если нашёлся маршрут или статический файл |
Частая ошибка
Путь /posts в before совпадает только с адресом /posts, а не со всем, что начинается на него:
// src/main/java/com/example/blog/Main.java
config.routes.before("/posts", ctx -> System.out.println("before /posts: " + ctx.path()));Запрос /posts печатает строку, а запрос /posts/1 — нет. Чтобы охватить вложенные адреса, добавьте второй обработчик с путём /posts/*.