1. 概要

多くの場合、Webアプリケーションは、いくつかのユースケースを満たすためにユーザー入力に依存しています。 その結果、フォームの送信は、そのようなアプリのデータを収集して処理するために頻繁に使用されるメカニズムです。

このチュートリアルでは、Springのフラッシュ属性がフォーム送信ワークフローを安全かつ確実に支援する方法を学習します。

2. フラッシュ属性の基本

フラッシュ属性を快適に使用する前に、フォーム送信ワークフローといくつかの重要な関連概念について十分なレベルの理解を深める必要があります。

2.1. 投稿/リダイレクト/パターンの取得

Webフォームを設計するための素朴な方法は、送信を処理し、その応答を通じて確認を返す単一の HTTPPOSTリクエストを使用することです。 ただし、このような設計では、ユーザーがページを更新することになった場合に、POST要求が重複して処理されるリスクがあります。

重複処理の問題を軽減するために、ワークフローを特定の順序(POST、REDIRECT、およびGET )で相互接続された要求のシーケンスとして作成できます。 要するに、これをフォーム送信用の投稿/リダイレクト/取得(PRG)パターンと呼びます。

POST要求を受信すると、サーバーはそれを処理し、制御を転送してGET要求を作成します。 続いて、GETリクエストの応答に基づいて確認ページが表示されます。 理想的には、最後のGETリクエストが複数回試行された場合でも、悪影響がないはずです。

2.2. フラッシュ属性のライフサイクル

PRG パターンを使用してフォームの送信を完了するには、リダイレクト後に最初のPOSTリクエストから最後のGETリクエストに情報を転送する必要があります。

残念ながら、どちらも使用できません RequestAttributes また、 SessionAttributes。 これは、前者は異なるコントローラー間でのリダイレクトに耐えられないのに対し、後者はフォームの送信が終了した後もセッション全体にわたって存続するためです。

ただし、SpringのWebフレームワークは、この正確な問題を修正できるフラッシュ属性を提供するため、心配する必要はありません。

プロジェクトでフラッシュ属性を使用するのに役立つRedirectAttributesインターフェイスのメソッドを見てみましょう。

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

RedirectAttributes addFlashAttribute(Object attributeValue);

Map<String, ?> getFlashAttributes();

フラッシュ属性は短命です。 そのため、これらはリダイレクトの直前に、基礎となるストレージに一時的に保存されます。 それらはリダイレクト後の後続のリクエストで引き続き使用可能であり、その後はなくなります。

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でのみ使用することもできます。

さらに、すべてのリクエストには2つの FlashMap インスタンスがあります。つまり、入力FlashMapと出力FlashMapで、PRGパターンで重要な役割を果たします。

  • 出力FlashMapは、POST要求で使用され、フラッシュ属性を一時的に保存して、リダイレクト後に次のGET要求に送信します。
  • 入力FlashMapは、リダイレクト前に前のPOST要求によって送信された読み取り専用フラッシュ属性にアクセスするために、最後のGET要求で使用されます

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抽象ユーティリティクラスで使用可能ないくつかの静的メソッドについて理解しましょう。

このチュートリアルの範囲内に焦点を合わせるために、フラッシュ属性に関連するメソッドにカバレッジを制限します。

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構成

シンプルなHTMLテンプレートを使用して動的なWebページを作成するためのJavaテンプレートエンジンであるThymeleafを使用します。

まず、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;
}

さらに、 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. フォームを作成

これで、送信フォームを作成する準備が整いました。 そのためには、フォームをユーザーに表示するためのGETリクエストを提供するエンドポイント/ poem/submitが必要です。

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

ここでは、ユーザーから提供された詩固有のデータを保持するためのコンテナーとしてモデルを使用しました。 さらに、 submitGet メソッドは、submitビューによって提供されるビューを返します。

さらに、POSTフォームをモデル属性poemにバインドします。

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

3.4. 投稿/リダイレクト/送信フローの取得

それでは、フォームのPOSTアクションを有効にしましょう。 これを行うには、PoemSubmissionコントローラーに / 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エンドポイントに転送されます。 また、リダイレクトを開始する前に、詩のデータをフラッシュ属性として追加しました。

次に、ユーザーに確認ページを表示する必要があるので、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";
    }
}

ここで重要なのは、成功ページにリダイレクトする前に、FlashMapを検証する必要があるということです。

最後に、成功ページ内のフラッシュ属性を使用して、ユーザーが送信した詩のタイトルを表示しましょう。

<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パターンとフラッシュ属性に関するいくつかの概念を学びました。 また、SpringBootWebアプリケーションで単純なフォーム送信を使用してフラッシュ属性が動作していることも確認しました。

いつものように、チュートリアルの完全なソースコードは、GitHubから入手できます。