endokのブログ

IT・プログラミングネタ

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を使った方がわかりやすい気がする。

参考記事:
Spring-MVCで画面遷移させるControllerメソッドになやんだ - おぼえがき

リクエスト情報を利用する

リクエストパラメータの受け取り

クエリストリングや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>

実行すると下記のようになる。

f:id:endok:20160606031808j:plain

ビューの共通部分を別ファイル化する(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動作を確認した。
いまのところ、必要な機能は問題なく揃っているようである。