Spring Boot触ってみる その2 ーController,テンプレートエンジン(Thymeleaf)ー
Spring Boot触ってみる その1 ー環境構築・サンプルプロジェクト作成ー - endokのブログ
の続き。
Controller作成と、Thymeleafを使ったテンプレート描画部分を確認する。
Spring BootではJSPではなくThymeleafの方がベタな模様。
(他のJava製Webフレームワークではどうなんでしょう)
前回作成したサンプルプロジェクトを引き続き利用する。
目次
事前準備
Thymeleafを利用可能にするため、pom.xmlの
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency>
また、Thymeleafはデフォルトだとxhtml形式でないとエラーになってしまい面倒なので、LEGACYHTML5モードで実行する。
そのために必要なNekoHTMLと設定をと追加する。
・pom.xml
<dependency> <groupId>net.sourceforge.nekohtml</groupId> <artifactId>nekohtml</artifactId> </dependency>
・application.properties
spring.thymeleaf.mode=LEGACYHTML5
[Maven]→[Update project...]も実行しておく。
単純なテンプレートを使ったコントローラー
コントローラー
単純にテンプレートを返すコントローラーは下記のようになる。
@RestControllerでなく@Controllerを利用する。
package me.endok.springboot; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @Controller public class HelloController { @RequestMapping("/hello") public String index() { return "hello"; } }
テンプレート
src/main/resources/templatesにテンプレートファイルを用意する。
・hello.html
<!DOCTYPE html> <html lang="ja" xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>Spring Boot Sample Site</title> </head> <body> <div>Hello World.</div> </body> </html>
xmlns:thを書いておくとSTS上でThymeleaf用属性が警告扱いにならない。
起動してhttp://localhost:8080/helloにアクセスすると、"Hello World."と表示される。
テンプレートに変数を渡す
Modelの利用
コントローラーでテンプレート用の変数をセットしてみる。
・HelloController.java
@RequestMapping("/hello") public String index(Model model) { model.addAttribute("target", "Japan"); return "hello"; }
テンプレート側では下記のように利用する。
・hello.html
<div th:text="'Hello, ' + ${target} + '.'"></div>
実行してアクセスすると、"Hello Japan."と表示される。
ModelAndViewの利用
さきほどはModelに値をセット、戻り値でビュー名を返したが、
ModelAndViewクラスを利用すると値とビュー名を一緒に返すことができる。
@RequestMapping("/hello") public ModelAndView index() { ModelAndView mav = new ModelAndView(); mav.addObject("target", "America"); mav.setViewName("hello"); return mav; }
ModelとModelAndViewの使い分け
いまのところどちらがよいという感覚は無いが、ModelAndViewを使った方が複雑なことができそう。(Controllerで複雑なことをすることは多くなさそうだが)
シンプルな場合はModelを使った方がわかりやすい気がする。
リクエスト情報を利用する
リクエストパラメータの受け取り
クエリストリングやPOSTパラメータをコントローラーで引数として受け取ることができる。
@RequestParam アノテーションを利用する。
@RequestMapping("/hello") public String index(@RequestParam String name, Model model) { model.addAttribute("target", name); return "hello"; }
これで"http://localhost:8080/hello?name=Taro"にアクセスすると"Hello, Taro."と表示される。
また上記の場合はパラメータなしでアクセスするとBad Requestエラーになってしまうが、@RequestParamアノテーションに設定パラメータを渡すこともできる。
デフォルト値の指定も可能。
@RequestMapping("/hello") public String index(@RequestParam(value = "name", required = false) String name, Model model) { model.addAttribute("target", (name == null ? "NoName" : name)); return "hello"; }
パスパラメータの受け取り
URL(パス)の一部をパラメータとして受け取ることができる。
@PathVariableアノテーションを利用する。
@RequestMapping("/hello/{name}") public String index(@PathVariable("name") String name, Model model) { model.addAttribute("target", name); return "hello"; }
フォームの受け取り
パラメータをまとめてPOJOクラスで受け取ることができる。
@ModelAttributeアノテーションを利用する。
・SampleForm.java(フォームクラス)
package me.endok.springboot; public class SampleForm { private String name; private Integer age; public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } }
フォーム用クラスにはセッターを用意しておく必要があるようで、public変数のみ用意した場合はうまく値が設定されなかった。
・コントローラー
@RequestMapping(value = "/hello") public String index(@ModelAttribute SampleForm sampleForm) { return "hello"; }
@ModelAttributeを指定した変数のプロパティに同名のパラメータが自動セットされる。
また、ビュー用変数にも自動セットされるため、コントローラー側で特に処理せずにビューで利用できる。
・ビュー
<h1>フォーム</h1> <form method="post" action="/hello"> 名前:<input type="text" name="name"><br> 年齢:<input type="text" name="age"><br> <input type="submit"> </form> <h1>表示</h1> <div th:text="'名前: ' + ${sampleForm.name}"></div> <div th:text="'年齢: ' + ${sampleForm.age}"></div>
実行すると下記のようになる。
ビューの共通部分を別ファイル化する(th:include,th:replace)
ヘッダ、フッタなどの共通化をするためにinclude機能が利用できる。
例えば、下記のようなテンプレートを考える。
ページ毎にフッターを書くのは効率・保守性が悪いため共通部分として定義したい。
・表示したいHTML
<!DOCTYPE html> <html lang="ja" xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>Spring Boot Sample Site</title> </head> <body> <div> <p>本文</p> </div> <footer> <p>ここはフッターです。</p> <ul> <li>フッター要素1</li> <li>フッター要素2</li> <li>フッター要素3</li> </ul> </footer> </body> </html>
共通部分を別ファイルに分離し、それをincludeするようにする。
・footer.html
<footer> <p>ここはフッターです。</p> <ul> <li>フッター要素1</li> <li>フッター要素2</li> <li>フッター要素3</li> </ul> </footer>
・hello.html
<!DOCTYPE html> <html lang="ja" xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>Spring Boot Sample Site</title> </head> <body> <div> <p>本文</p> </div> <div th:include="footer"></div> </body> </html>
このようにすると、下記HTMLが出力される。
<!DOCTYPE html> <html lang="ja" xmlns="http://www.w3.org/1999/xhtml"> <head> <meta charset="UTF-8" /> <title>Spring Boot Sample Site</title> </head><body> <div> <p>本文</p> </div> <div><footer> <p>ここはフッターです。</p> <ul> <li>フッター要素1</li> <li>フッター要素2</li> <li>フッター要素3</li> </ul> </footer></div> </body></html>
出力用のdivタグが残ってしまうが、それを無くしたい場合はth:replaceを利用する。
別ファイル化の一部分を読み込む(th:fragment)
別ファイル全体でなく、一部分をfragmentとして定義して読み込むこともできる。
・common.html
<!DOCTYPE html> <html lang="ja" xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>Spring Boot Sample Site</title> </head> <body> <header th:fragment="header"> <p>ここはヘッダーです。</p> <ul> <li>ヘッダー要素1</li> <li>ヘッダー要素2</li> <li>ヘッダー要素3</li> </ul> </header> <footer th:fragment="footer"> <p>ここはフッターです。</p> <ul> <li>フッター要素1</li> <li>フッター要素2</li> <li>フッター要素3</li> </ul> </footer> </body> </html>
・hello.html
<!DOCTYPE html> <html lang="ja" xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>Spring Boot Sample Site</title> </head> <body> <div th:replace="common :: header"></div> <div> <p>本文</p> </div> <div th:replace="common :: footer"></div> </body> </html>
このようにすることで、共通部分ファイル単独でもHTMLとして表示可能になるメリットがある。
まとめ
コントローラーへの入出力、Thymeleafでの変数表示、include動作を確認した。
いまのところ、必要な機能は問題なく揃っているようである。