Java: Веб-технологии

Теория: Флеш-сообщения

Работая на Хекслете, вы не раз видели сообщение о результатах выполнения любого действия — будь то аутентификация, регистрация или вступление в курс. Выглядят эти сообщения так:

Flash Message

В веб-разработке это называется флеш-сообщениями. Они обычно используются после редиректа для индикации успешности или неудачи предыдущего действия. Например, после регистрации сайт выполняет перенаправление на страницу, с которой пришел пользователь, а затем выводит сообщение «Регистрация выполнена».

Особенность флеш-сообщений в том, что они появляются только один раз и пропадают после обновления страницы.

Иногда фреймворки поддерживают флеш-сообщения самостоятельно или с помощью соответствующих дополнений. В случае Java-фреймворков таких решений нет, но их легко имитировать с помощью механизма сессий. В Javalin нам для этого понадобится два метода:

// Этот метод устанавливает в сессию значение по ключу
ctx.sessionAttribute("key", "value");
// Этот метод читает сообщение из сессии и тут же удаляет его
ctx.consumeSessionAttribute("key");

Метод consumeSessionAttribute() идеально подходит для реализации сессии. Он читает сообщение из сессии и удаляет его, что автоматически прячет сообщение при обновлении страницы или клику по любой ссылке.

Добавим флеш-сообщение в процесс создания курса. Для этого нам понадобится установить сообщение, которое будет выводиться после успешного создания курса:

app.post(NamedRoutes.coursesPath(), ctx -> {
    var name = ctx.formParam("name");
    var description = ctx.formParam("description");

    var course = new Course(name, description);
    CourseRepository.save(course);
    // Добавляем сообщение в сессию
    // Ключ может иметь любое название, здесь мы выбрали flash
    ctx.sessionAttribute("flash", "Course has been created!");
    ctx.redirect(NamedRoutes.coursesPath());
});

Сразу после установки сообщения выполняется редирект и загрузка какой-то другой страницы — в нашем случае это список курсов. Это означает, что вывод сообщения должен происходить на этом списке. Для этого нам понадобится извлечь сообщение из сессии, передать его в шаблон и вывести:

app.get(NamedRoutes.coursesPath(), ctx -> {
    String flash = ctx.consumeSessionAttribute("flash");
    // Добавляем flash в определение CoursesPage
    var page = new CoursesPage(courses, term, flash);

    ctx.render("courses/index.jte", model("page", page));
});

Метод ctx.consumeSessionAttribute() извлекает данные по указанному ключу и удаляет их. Это важно, потому что повторная загрузка страницы приведет к пропаданию флеш-сообщения. Дальше сообщение передается в CoursePage. Осталось сделать вывод в шаблоне:

@if(page.getFlash() != null)
    <p>${page.getFlash()}</p>
@endif

Использование макета

Логика вывода флеш-сообщений идентична для всех страниц сайта, поэтому перенесем их вывод в макет. Но как их передать туда с учетом, что такие сообщения передаются в шаблон в разных Page объектах?

Самый простой способ — это создание базового класса для всех Page, в котором сосредоточится логика работы с флеш-сообщениями:

// src/main/java/org/example/hexlet/dto/BasePage.java
package org.example.hexlet.dto;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class BasePage {
    private String flash;
}

Класс CoursesPage при этом будет наследоваться от базового класса Page

package org.example.hexlet.dto.courses;

import org.example.hexlet.dto.BasePage;
import lombok.AllArgsConstructor;
import lombok.Getter;
// остальные импорты

@AllArgsConstructor
@Getter
public class CoursesPage extends BasePage {
    private List<Course> courses;
    private String term;
}

Теперь в нужном месте мы можем добавлять сообщения так:

app.get(NamedRoutes.coursesPath(), ctx -> {
    var page = new CoursesPage(courses, term);
    page.setFlash(ctx.consumeSessionAttribute("flash"));
    ctx.render("courses/index.jte", model("page", page));
});

Затем мы должны передать этот page в макет в шаблоне этого обработчика:

@import org.example.hexlet.util.NamedRoutes
@import org.example.hexlet.dto.courses.CoursesPage
@param CoursesPage page

@template.layout.page(
    page = page,
    content = @`
      Тут шаблон
    `
)

И наконец вывод в макете:

@import org.example.hexlet.util.NamedRoutes
@import org.example.hexlet.dto.BasePage // Импортируем базовый класс
@import gg.jte.Content
@param Content content
@param BasePage page = null // Если не передали, то игнорируем

<!doctype html>
<html lang="en">
    <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <title>Hexlet Javalin Example</title>
    </head>
    <body>
        @if(page != null && page.getFlash() != null)
            <p>${page.getFlash()}</p>
        @endif
        ${content}
    </body>
</html>

Рекомендуемые программы

Завершено

0 / 23