Перейти к основному содержимому

Руководство по атрибутам Flash в веб-приложении Spring

· 7 мин. чтения

1. Обзор

Веб-приложения часто зависят от пользовательского ввода для выполнения нескольких вариантов их использования. В результате отправка формы является широко используемым механизмом для сбора и обработки данных для таких приложений.

В этом руководстве мы узнаем, как flash-атрибуты Spring могут помочь нам в безопасном и надежном рабочем процессе отправки форм.

2. Основы Flash-атрибутов

Прежде чем мы сможем комфортно использовать flash-атрибуты, нам необходимо достичь приличного уровня понимания рабочего процесса отправки формы и нескольких ключевых связанных с ним концепций.

2.1. Опубликовать/перенаправить/получить шаблон

Наивным способом разработки веб-формы было бы использование одного HTTP-запроса POST , который обеспечивает отправку и возвращает подтверждение в своем ответе. Однако такой дизайн подвергает риску двойную обработку POST-запросов, если пользователь в конечном итоге обновит страницу.

Чтобы смягчить проблему дублирования обработки, мы можем создать рабочий процесс как последовательность взаимосвязанных запросов в определенном порядке, а именно POST, REDIRECT и GET . Короче говоря, мы называем это шаблоном Post/Redirect/Get (PRG) для отправки формы.

Получив запрос POST, сервер обрабатывает его, а затем передает управление для выполнения запроса GET. Впоследствии страница подтверждения отображается в зависимости от ответа на запрос GET. В идеале, даже если последний запрос GET будет предпринят более одного раза, не должно быть каких-либо неблагоприятных побочных эффектов.

2.2. Жизненный цикл атрибутов Flash

Чтобы завершить отправку формы с использованием шаблона PRG , нам потребуется передать информацию из начального запроса POST в окончательный запрос GET после перенаправления.

К сожалению, мы не можем использовать ни RequestAttributes , ни SessionAttributes . Это связано с тем, что первый не выдержит перенаправления между разными контроллерами, а второй будет действовать в течение всего сеанса даже после завершения отправки формы.

Но нам не нужно беспокоиться, поскольку веб-инфраструктура Spring предоставляет атрибуты flash, которые могут решить именно эту проблему.

Давайте посмотрим на методы в интерфейсе RedirectAttributes , которые могут помочь нам использовать атрибуты flash в нашем проекте:

RedirectAttributes addFlashAttribute(String attributeName, @Nullable Object attributeValue);

RedirectAttributes addFlashAttribute(Object attributeValue);

Map<String, ?> getFlashAttributes();

Атрибуты Flash недолговечны . Таким образом, они временно хранятся в некотором базовом хранилище непосредственно перед перенаправлением. Они остаются доступными для последующего запроса после редиректа, а потом исчезают.

2.3. Структура данных FlashMap

Spring предоставляет абстрактную структуру данных под названием FlashMap для хранения атрибутов флэш-памяти в виде пар ключ-значение.

Давайте посмотрим на определение класса FlashMap :

public final class FlashMap extends HashMap<String, Object> implements Comparable<FlashMap> {

@Nullable
private String targetRequestPath;

private final MultiValueMap<String, String> targetRequestParams
= new LinkedMultiValueMap<>(4);

private long expirationTime = -1;
}

Мы можем заметить, что класс FlashMap наследует свое поведение от класса HashMap . Таким образом, экземпляр FlashMap может хранить сопоставление ключ-значение атрибутов . Кроме того, мы можем привязать экземпляр FlashMap для использования только определенным URL-адресом перенаправления.

Кроме того, каждый запрос имеет два экземпляра FlashMap , а именно Input FlashMap и Output FlashMap, которые играют важную роль в шаблоне PRG:

  • Выходная FlashMap используется в запросе POST для временного сохранения атрибутов флэш-памяти и отправки их в следующий запрос GET после перенаправления.
  • Входная карта FlashMap используется в последнем запросе GET для доступа к доступным только для чтения атрибутам флэш-памяти, которые были отправлены предыдущим запросом POST перед перенаправлением.

2.4. FlashMapManager и RequestContextUtils

Как следует из названия, мы можем использовать FlashMapManager для управления экземплярами FlashMap .

Во-первых, давайте взглянем на определение этого интерфейса стратегии:

public interface FlashMapManager {

@Nullable
FlashMap retrieveAndUpdate(HttpServletRequest request, HttpServletResponse response);

void saveOutputFlashMap(FlashMap flashMap, HttpServletRequest request, HttpServletResponse response);
}

Проще говоря, мы можем сказать, что FlashMapManager позволяет нам читать, обновлять и сохранять экземпляры FlashMap в каком-то базовом хранилище.

Далее давайте ознакомимся с несколькими статическими методами, доступными в абстрактном служебном классе RequestContextUtils .

Чтобы не выходить за рамки этого руководства, мы ограничим наше рассмотрение методами, относящимися к атрибутам flash:

public static Map<String, ?> getInputFlashMap(HttpServletRequest request);

public static FlashMap getOutputFlashMap(HttpServletRequest request);

public static FlashMapManager getFlashMapManager(HttpServletRequest request);

public static void saveOutputFlashMap(String location,
HttpServletRequest request, HttpServletResponse response);

Мы можем использовать эти методы для извлечения входных/выходных экземпляров FlashMap , получения FlashMapManager для запроса и сохранения экземпляра FlashMap .

3. Пример использования отправки формы

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

Наше приложение для поэтического конкурса имеет простой вариант использования: он принимает стихи от разных поэтов, отправляя форму. Кроме того, конкурсная работа будет содержать необходимую информацию, связанную со стихотворением, такую как название, основная часть и имя автора.

3.1. Конфигурация листа тимьяна

Мы будем использовать Thymeleaf , механизм шаблонов Java для создания динамических веб-страниц с помощью простых шаблонов HTML.

Во- первых, нам нужно добавить зависимость spring-boot-starter-thymeleaf в pom.xml нашего проекта :

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
<version>2.2.1.RELEASE</version>
</dependency>

Затем мы можем определить некоторые свойства, специфичные для Thymeleaf, в нашем файле pplication.properties , расположенном в каталоге src/main/resources :

spring.thymeleaf.cache=false
spring.thymeleaf.enabled=true
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html

Определив эти свойства, теперь мы можем создавать все наши представления в каталоге /src/main/resources/templates . В свою очередь, Spring добавит суффикс .html ко всем представлениям, названным внутри нашего контроллера.

3.2. Модель домена

Далее давайте определим нашу модель предметной области в классе Poem :

public class Poem {
private String title;
private String author;
private String body;
}

Кроме того, мы можем добавить статический метод isValidPoem() в наш класс Poem , чтобы помочь нам проверить, что поля не допускают пустые строки:

public static boolean isValidPoem(Poem poem) {
return poem != null && Strings.isNotBlank(poem.getAuthor())
&& Strings.isNotBlank(poem.getBody())
&& Strings.isNotBlank(poem.getTitle());
}

3.3. Создать форму

Теперь мы готовы создать нашу форму отправки. Для этого нам нужна конечная точка /poem/submit , которая будет обслуживать запрос GET, чтобы показать форму пользователю:

@GetMapping("/poem/submit")
public String submitGet(Model model) {
model.addAttribute("poem", new Poem());
return "submit";
}

Здесь мы использовали модель в качестве контейнера для хранения специфических для стихотворения данных, предоставленных пользователем. Кроме того, метод submitGet возвращает представление, обслуживаемое представлением отправки .

Кроме того, мы хотим связать форму POST с атрибутом модели стихотворение :

<form action="#" method="post" th:action="@{/poem/submit}" th:object="${poem}">
<!-- form fields for poem title, body, and author -->
</form>

3.4. Поток отправки/перенаправления/получения отправки

Теперь давайте включим действие POST для формы. Для этого мы создадим конечную точку /poem/submit в контроллере PoemSubmission для обслуживания запроса POST :

@PostMapping("/poem/submit")
public RedirectView submitPost(
HttpServletRequest request,
@ModelAttribute Poem poem,
RedirectAttributes redirectAttributes) {
if (Poem.isValidPoem(poem)) {
redirectAttributes.addFlashAttribute("poem", poem);
return new RedirectView("/poem/success", true);
} else {
return new RedirectView("/poem/submit", true);
}
}

Мы можем заметить, что если отправка прошла успешно, то управление переходит к конечной точке /poem/success . Кроме того, мы добавили данные стихотворения в качестве флэш-атрибута перед запуском перенаправления.

Теперь нам нужно показать пользователю страницу подтверждения, поэтому давайте реализуем функциональность для конечной точки /poem/success , которая будет обслуживать запрос GET:

@GetMapping("/poem/success")
public String getSuccess(HttpServletRequest request) {
Map<String, ?> inputFlashMap = RequestContextUtils.getInputFlashMap(request);
if (inputFlashMap != null) {
Poem poem = (Poem) inputFlashMap.get("poem");
return "success";
} else {
return "redirect:/poem/submit";
}
}

Здесь важно отметить, что нам нужно проверить FlashMap , прежде чем мы решим перенаправить на страницу успеха .

Наконец, давайте воспользуемся атрибутом flash на нашей странице успеха, чтобы отобразить название стихотворения , отправленного пользователем:

<h1 th:if="${poem}">
<p th:text="${'You have successfully submitted poem titled - '+ poem?.title}"/>
Click <a th:href="@{/poem/submit}"> here</a> to submit more.
</h1>

4. Вывод

В этом руководстве мы изучили несколько концепций шаблона Post/Redirect/Get и атрибутов flash. И мы также видели атрибуты flash в действии с простой отправкой формы в веб-приложении Spring Boot.

Как всегда, полный исходный код руководства доступен на GitHub .