Spring Web 应用程序中的 Flash 属性指南

1. 概述

Web 应用程序通常依赖于用户输入来满足其多个用例。因此,表单提交是一种大量使用的机制,用于收集和处理此类应用的数据。

在本教程中,我们将了解 Spring 的 flash 属性如何帮助我们安全可靠地完成表单提交工作流程

2. 闪光属性基础知识

在我们可以轻松使用 flash 属性之前,我们需要对表单提交工作流程和一些关键相关概念有很好的理解。

2.1. 发布/重定向/获取模式

设计 Web 表单的一种天真方法是使用单个 HTTP POST 请求来处理提交并通过其响应返回确认。但是,这种设计暴露了重复处理 POST 请求的风险,以防用户最终刷新页面。

为了减少重复处理的问题,我们可以将工作流创建为按特定顺序(即 POST、重定向和 GET)的互连请求序列。简而言之,我们称之为表单提交的发布/重定向/获取 (PRG) 模式。

收到 POST 请求后,服务器会处理它,然后转移控制权以发出 GET 请求。随后,根据 GET 请求的响应显示确认页面。理想情况下,即使最后一个GET请求被多次尝试,也不应该有任何不利的副作用。

2.2. 闪存属性的生命周期

要使用 PRG 模式完成表单提交,我们需要在重定向后将信息从初始 POST 请求传输到最终 GET 请求。

不幸的是,我们既不能使用 RequestAttributes,也不能使用 SessionAttributes。 这是因为前者不会在不同的控制器之间重定向后幸存下来,而后者即使在表单提交结束后也会持续整个会话。

但是,我们不需要担心,因为Spring的Web框架提供了可以解决这个确切问题的flash属性。

让我们看看 RedirectAttributes 接口中可以帮助我们在项目中使用 flash 属性的方法:

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

RedirectAttributes addFlashAttribute(Object attributeValue);

Map<String, ?> getFlashAttributes();

闪存属性的生存期很短。因此,它们在重定向之前临时存储在某个基础存储中。重定向后,它们仍可用于后续请求,然后它们就消失了。

2.3. 闪图数据结构

Spring 提供了一个名为 FlashMap 的抽象数据结构,用于将 flash 属性存储为键值对。

让我们看一下 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 实例,即输入 FlashMap 和 Output FlashMap,它们在 PRG 模式中起着重要作用:

  • 输出闪光图在 POST 请求中用于临时保存闪光属性,并在重定向后将它们发送到下一个 GET 请求
  • 输入闪光图在最终的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. 表单提交用例

到目前为止,我们已经对围绕闪光属性的不同概念有了基本的了解。因此,让我们进一步,在诗歌比赛 Web 应用程序中使用它们。

我们的诗歌比赛应用程序有一个简单的用例,通过提交表格接受来自不同诗人的诗歌作品。此外,参赛作品将具有与诗歌相关的必要信息,例如标题、正文和作者姓名。

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>

接下来,我们可以在位于 src/main/resources 目录中的pplication.properties 文件中定义一些特定于 Thymeleaf 的属性:

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;
}

此外,我们可以在 Poem 类中添加 isValidPoem() 静态方法,以帮助我们验证字段是否不允许空字符串:

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";
}

在这里,我们使用一个模型作为容器来保存用户提供的特定于诗歌的数据。此外,提交获取方法返回由提交视图提供的视图。

此外,我们希望将 POST 表单与模型属性绑定:

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

3.4. 发布/重定向/获取提交流程

现在,让我们为表单启用 POST 操作。为此,我们将在 PoemSubmit 控制器中创建 /poem/submit 端点来为 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 端点。此外,我们在启动重定向之前将诗歌数据添加为 flash 属性。

现在,我们需要向用户显示一个确认页面,因此让我们实现将为 GET 请求提供服务的 /poem/success 端点的功能:

@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";
    }
}

这里需要注意的是,在我们决定重定向到成功页面之前,我们需要验证闪光图

最后,让我们使用成功页面中的 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. 结论

在本教程中,我们学习了有关发布/重定向/获取模式和闪存属性的一些概念。而且,我们还看到了 flash 属性在 Spring Boot Web 应用程序中通过简单的表单提交而起作用。

与往常一样,本教程的完整源代码可在 GitHub 上找到。

猜你喜欢

转载自blog.csdn.net/allway2/article/details/128217900