モーダルを閉じる工作HardwareHub ロゴ画像

工作HardwareHubは、ロボット工作や電子工作に関する情報やモノが行き交うコミュニティサイトです。さらに詳しく

利用規約プライバシーポリシー に同意したうえでログインしてください。

Spring Boot フォーム関連のサンプルコード (Thymeleaf)

モーダルを閉じる

ステッカーを選択してください

お支払い手続きへ
モーダルを閉じる

お支払い内容をご確認ください

購入商品
」ステッカーの表示権
メッセージ
料金
(税込)
決済方法
GooglePayマーク
決済プラットフォーム
確認事項

利用規約をご確認のうえお支払いください

※カード情報はGoogleアカウント内に保存されます。本サイトやStripeには保存されません

※記事の執筆者は購入者のユーザー名を知ることができます

※購入後のキャンセルはできません

作成日作成日
2017/05/31
最終更新最終更新
2021/10/04
記事区分記事区分
一般公開

Spring Boot のテンプレートエンジンとしては、Thymeleaf が有名です。本ページでは、フォーム関連の処理について、基本的なサンプルコードをまとめます。Rails におけるビューヘルパーや、フォーム入力値のバリデーションに相当する機能です。

公式ドキュメント

サンプルプロジェクト構成

.
|-- build.gradle
|-- gradle
|   `-- wrapper
|       |-- gradle-wrapper.jar
|       `-- gradle-wrapper.properties
|-- gradlew
|-- gradlew.bat
`-- src
    `-- main
        |-- java
        |   `-- hello
        |       |-- Application.java
        |       |-- Greeting.java
        |       `-- GreetingController.java
        `-- resources
            `-- templates
                |-- greeting.html
                `-- result.html

Java ソースコード

Greeting.java

コントローラとビューで値のやり取りを行うための、入れ物となるクラスです。idcontent は HTML ファイル内の form 内の input に対応しています。

package hello;

public class Greeting {

    private long id;
    private String content;

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }
}

GreetingController.java

@RequestMapping(path = "/greeting", method = RequestMethod.GET) に相当するアノテーション @GetMapping("/greeting") を利用しています。@GetMapping 側のアクションについては、Model model に空の Greeting オブジェクトを格納して greeting ビューで利用しています。@PostMapping 側のアクションについては、@ModelAttribute が付与された Greeting greeting に POST されたパラメータが自動で格納されており、result ビューで利用されます。

package hello;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;

@Controller
public class GreetingController {

    @GetMapping("/greeting")
    public String greetingForm(Model model) {
        model.addAttribute("greeting", new Greeting());
        return "greeting";
    }

    @PostMapping("/greeting")
    public String greetingSubmit(@ModelAttribute Greeting greeting) {
        // return "greeting"; // 同じビューを使い回すこともできます。
        return "result";
    }
}

HTML ファイル

greeting.html

@{} を利用してリンクすることで、例えばアプリケーション全体が /myapp といったプレフィックス以下で動作する場合にも対応できます。また、th:field はモデルのメンバ変数と input をバインドするための設定です。

Relative URLs starting with / (eg: /order/details) will be automatically prefixed by the application context name.
http://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html#link-urls

<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
  <title>Getting Started: Handling Form Submission</title>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
  <h1>Form</h1>
  <form action="#" th:action="@{/greeting}" th:object="${greeting}" method="post">
    <p>Id: <input type="text" th:field="*{id}" /></p>
    <p>Message: <input type="text" th:field="*{content}" /></p>
    <p><input type="submit" value="Submit" /> <input type="reset" value="Reset" /></p>
  </form>
</body>
</html>

result.html

POST された値を Thymeleaf の記法にしたがって設定して表示します。ここまで内容は http://localhost:8080/greeting にアクセスして動作確認できます。

<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
  <title>Getting Started: Handling Form Submission</title>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
  <h1>Result</h1>
  <p th:text="'id: ' + ${greeting.id}" />
  <p th:text="'content: ' + ${greeting.content}" />
  <a href="/greeting">Submit another message</a>
</body>
</html>

チェックボックス

チェックボックスが一つの場合

input タグには id が自動で付与されて、th:for から参照されます。

greeting.html

<div>
  <label th:for="${#ids.next('admin')}">Admin</label>
  <input type="checkbox" th:field="*{admin}" />
</div>

Greeting.java

private boolean isAdmin;

public boolean isAdmin() {
    return isAdmin;
}

public void setAdmin(boolean isAdmin) {
    this.isAdmin = isAdmin;
}

チェックボックスが複数の場合

先程の例の #ids.next() ではなく #ids.prev() を用いると、label タグを input タグの後ろに設置できます。また、先程の例の isAdmin と異なり、今回の features は boolean ではないため、複数チェックボックスに対してそれぞれ th:value で値を設定しています。

greeting.html

<ul>
  <li th:each="feat : ${allFeatures}">
    <input type="checkbox" th:field="*{features}" th:value="${feat}" />
    <label th:for="${#ids.prev('features')}" 
           th:text="${feat}">feature label</label>
  </li>
</ul>

Greeting.java

private List<String> features;

public List<String> getFeatures() {
    return features;
}

public void setFeatures(List<String> features) {
    this.features = features;
}

GreetingController.java

@GetMapping("/greeting")
public String greetingForm(Model model) {
    model.addAttribute("greeting", new Greeting());
    model.addAttribute("allFeatures", Arrays.asList("xxx", "yyy"));
    return "greeting";
}

@PostMapping("/greeting")
public String greetingSubmit(@ModelAttribute Greeting greeting, Model model) {
    greeting.setId(greeting.getId() + 1);
    model.addAttribute("allFeatures", Arrays.asList("xxx", "yyy"));
    return "greeting";
}

ラジオボタン

ラジオボタンは、複数チェックボックスの場合と似た記法になります。ただし、モデルが保持する値は単数です。

greeting.html

<ul>
  <li th:each="feat : ${allFeatures}">
    <input type="radio" th:field="*{feature}" th:value="${feat}" />
    <label th:for="${#ids.prev('feature')}" th:text="${feat}">feature label</label>
  </li>
</ul>

Greeting.java

private String feature;

public String getFeature() {
    return feature;
}

public void setFeature(String feature) {
    this.feature = feature;
}

GreetingController.java

(複数チェックボックスの場合と同じであるため、省略。)

セレクトボックス

セレクトボックスはラジオボタンとほぼ同じ構造になります。以下の例において、Greeting.java と GreetingController.java はラジオボタンと同じであるため省略します。

greeting.html

<select th:field="*{feature}">
  <option th:each="feat : ${allFeatures}" 
          th:value="${feat}" 
          th:text="${feat}"></option>
</select>

基本的なフォームバリデーション

Validating Form Input に記載の org.hibernate:hibernate-validator は、org.springframework.boot:spring-boot-starter-thymeleaf が依存するため build.gradle に明記する必要はありません。依存関係は以下のコマンドで確認できます。

./gradlew dependencies

Greeting.java

バリデーションは Greeting.java で設定します。

import javax.validation.constraints.Max;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

上記ライブラリをインポートして、メンバ変数にアノテーションを設定します。@NotNull で null を許容しない設定になります。@Max による最大値設定や、@Size による文字列長の設定も可能です。

@NotNull
@Max(10)
private long id;

@NotNull
@Size(min=2, max=30)
private String content;

GreetingController.java

@ModelAttribute ではなく @Valid を設定することで、バリデーションが行われます。バリデーション結果 bindingResult によって、表示するビューを変更します。エラーがある場合は、エラーメッセージと伴に、再度フォームビューを表示します。その際、フォームにはユーザーが入力した値が設定された状態になります。

package hello;

import javax.validation.Valid;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;

@Controller
public class GreetingController {

    @GetMapping("/greeting")
    public String greetingForm(Model model) {
        model.addAttribute("greeting", new Greeting());
        return "greeting";
    }

    @PostMapping("/greeting")
    public String greetingSubmit(@Valid Greeting greeting, BindingResult bindingResult) {
        if (bindingResult.hasErrors()) {
            return "greeting";
        }
        return "result";
        // return "redirect:/somewhere"; // リダイレクトさせることもできます。
    }
}

greeting.html

エラーが存在する場合のみ表示される、th:errors を用いた以下の二行を追記します。複数のエラーが存在する場合は <br /> で区切られます。

<p th:if="${#fields.hasErrors('id')}" th:errors="*{id}">Id Error</p>
<p th:if="${#fields.hasErrors('content')}" th:errors="*{content}">Content Error</p>

th:each で個別にタグを設定することもできます。

<p th:each="err : ${#fields.errors('id')}" th:text="${err}"></p>
<p th:each="err : ${#fields.errors('content')}" th:text="${err}"></p>

全体としては以下のようになります。

<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
  <title>Getting Started: Handling Form Submission</title>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
  <h1>Form</h1>
  <form action="#" th:action="@{/greeting}" th:object="${greeting}" method="post">
    <p>Id: <input type="text" th:field="*{id}" /></p>
    <p th:if="${#fields.hasErrors('id')}" th:errors="*{id}">Id Error</p>
    <p>Message: <input type="text" th:field="*{content}" /></p>
    <p th:if="${#fields.hasErrors('content')}" th:errors="*{content}">Content Error</p>
    <p><input type="submit" value="Submit" /> <input type="reset" value="Reset" /></p>
  </form>
</body>
</html>

エラー時の CSS 設定

th:field が設定された input タグに対して、エラー時に特定の CSS クラスを設定することができます。th:errorclass を利用します。上書きではなく、クラスが追加されます。

<p>Id: <input type="text" th:field="*{id}" th:errorclass="has-error" /></p>

フォーム全体のエラー

input タグ毎ではなく、フォーム全体としてエラーを表示したい場合は * または all でエラー処理します。その際、form タグ内でしか利用できないことに注意します。

<ul th:if="${#fields.hasErrors('*')}">
  <li th:each="err : ${#fields.errors('*')}" th:text="${err}"></li>
</ul>

意味内容としては同じですが、専用の関数 hasAnyErrors()allErrors() を利用して書き換えると以下のようになります。

<ul th:if="${#fields.hasAnyErrors()}">
  <li th:each="err : ${#fields.allErrors()}" th:text="${err}"></li>
</ul>

<br /> 区切りで表示したい場合は以下のようにします。

<p th:if="${#fields.hasErrors('all')}" th:errors="*{all}"></p>

オブジェクトとしてより詳細に制御したい場合は以下のようにします。やはり form タグ内でしか利用できないことに注意します。

<ul>
  <li th:each="e : ${#fields.detailedErrors()}" th:unless="${e.global}">
    <span th:text="${e.fieldName}"></span> | <span th:text="${e.message}"></span>
  </li>
</ul>

フォームタグの外でエラーを表示

<ul th:if="${#fields.hasErrors('${greeting.*}')}">
  <li th:each="err : ${#fields.errors('${greeting.*}')}" th:text="${err}"></li>
</ul>

<form action="#" th:action="@{/greeting}" th:object="${greeting}" method="post">
...

エラーメッセージの日本語化

messages_{en|ja}.properties と同様に、ValidationMessages_{en|ja}.properties を利用したバリデーションメッセージの多言語対応ができます。以下のページの項目に対応する設定を行うことで、エラーメッセージをカスタマイズできます。

https://github.com/hibernate/hibernate-validator/blob/master/engine/src/main/resources/org/hibernate/validator/ValidationMessages.properties

ただし、Spring Boot のエンコーディング設定の関係で、日本語は文字化けしてしまいます。簡単な対応方法としては、native2asciiで ascii コードに変換することです。また、Eclipse を利用している場合、マーケットプレイスの Properties Editor をインストールすれば、閲覧編集が容易になります。

native2ascii -encoding UTF-8 src/main/resources/ValidationMessages_ja.properties src/main/resources/ValidationMessages_ja.properties

例えば以下のようになります。

src/main/resources/ValidationMessages_ja.properties

javax.validation.constraints.Size.message = {min} \u6587\u5b57\u4ee5\u4e0a {max} \u6587\u5b57\u4ee5\u4e0b\u3067\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044\u3002

src/main/resources/ValidationMessages_en.properties

javax.validation.constraints.Size.message = size must be between {min} and {max}

リダイレクト先にパラメータを引き継ぐ

コントローラで以下のように文字列を返すことで、302 リダイレクトを発生させることができます。

return "redirect:/somewhere";

その際、リダイレクト先にパラメータを渡すためには RedirectAttributes を利用します。Rails における flash / notice / alert に相当する機能です。

GreetingController.java

package hello;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

@Controller
public class GreetingController {

    @GetMapping("/greeting")
    public String greetingForm(Model model) {
        model.addAttribute("greeting", new Greeting());
        return "greeting";
    }

    @PostMapping("/greeting")
    public String greetingSubmit(@ModelAttribute Greeting greeting, RedirectAttributes redirectAttributes) {

        // 直接格納
        redirectAttributes.addFlashAttribute("id", greeting.getId());

        // 入れ物 modelMap を用意して格納
        ModelMap modelMap = new ModelMap();
        modelMap.addAttribute("content", greeting.getContent());
        redirectAttributes.addFlashAttribute("modelMap", modelMap);

        return "redirect:/somewhere";
    }

    @GetMapping("/somewhere")
    public String someWhere(@ModelAttribute("id") long id, @ModelAttribute("modelMap") ModelMap modelMap) {
        System.out.println(id);
        System.out.println(modelMap.get("content"));
        return "redirect:/greeting";
    }
}
Likeボタン(off)0
詳細設定を開く/閉じる
アカウント プロフィール画像

Spring Bootでの実用的な機能をわかりやすく解説中

記事の執筆者にステッカーを贈る

有益な情報に対するお礼として、またはコメント欄における質問への返答に対するお礼として、 記事の読者は、執筆者に有料のステッカーを贈ることができます。

>>さらに詳しくステッカーを贈る
ステッカーを贈る コンセプト画像

Feedbacks

Feedbacks コンセプト画像

    ログインするとコメントを投稿できます。

    ログインする

    関連記事

    • Spring Security フォームログインのサンプルコード
      Spring フレームワークによる Web アプリケーション開発で、ログイン処理を実装する際は Spring Security が便利です。ここでは特に Spring Boot で Web アプリケーションを開発する場合を対象とし、フォームによる ID/Password ログインを行うためのサンプルコードをまとめます。 公式ドキュメント [Spring Security チュートリアル](http...
      えびちゃんえびちゃん12/4/2019に更新
      いいねアイコン画像0
    • Java配列の宣言方法 (C/C++との違い)
      Javaの配列 Javaの配列宣言方法はC/C++と似ているようで若干異なる。 初期化しない場合 C/C++の int array[10]; はJavaでは int array[] = new int[10]; となる。同様にC/C++の int array[3][3]; はJavaでは int array[][] = new int[3][3]; となる。 初期化
      てんとうむしてんとうむし5/13/2018に更新
      いいねアイコン画像0
    • PlantUML による UML 図の描き方
      サムネイル画像-c788fffde5
      PlantUML はテキスト形式で表現されたシーケンス図やクラス図といった UML (Unified Modeling Language) 図の情報から画像を生成するためのツールです。簡単な使い方をまとめます。 インストール方法の選択 Atom や Eclipse のプラグインをインストールしてエディタから利用する方法、JAR をダウンロードして Java コマンドで実行する方法、Redmine ...
      kentakenta1/21/2020に更新
      いいねアイコン画像0
    • Akka HTTP サンプルコード (Scala)
      サムネイル画像-a98142497c
      Akka アクターを用いて実装された汎用 HTTP フレームワークです。Spray の後継です。コアモジュールである akka-http-core は 2016/2/17 に experimental が外れました。akka-http などのいくつかのサブモジュールは 2016/3/1 現在 experimental のままですが、基本的な
      雄太雄太10/7/2021に更新
      いいねアイコン画像0
    • Kestrel の使用例
      Kestrel は Message Queue (MQ) の実装のひとつです。一般に MQ はアプリケーション間やプロセス間、スレッド間で非同期に通信するために用いられます。メッセージの送信側は MQ に書き込めば受信側の応答を待たずに次の処理に非同期に進むことができます。Kestrel はわずか 2500 行程の Scala で実装されており JVM で動作します。MQ 自体はメモリ上に存在する...
      したくんしたくん10/12/2017に更新
      いいねアイコン画像0