SpringのWebFlux+Thymeleafで画面を表示する

# 概要
３月某日会社にて

* 仕事でプロトタイプを作成しているときに、大量のデータを一度に表示しそうな事案がある
* よろしいならば噂のWebFluxを利用してみよう
* Thymeleafで表示してみよう

ということで、調べたときのまとめです。
WebFluxのあれこれは後でまとめたい（ここの説明では省きます）

### 余談
[Spring Boot 2.0 goes GA](https://spring.io/blog/2018/03/01/spring-boot-2-0-goes-ga) 
とうとうSpring Bootが正規版で公開されましたね
利用が加速してドキュメントが増えてくれると嬉しいですね(｀･ω･´)

[Spring Initializr](https://start.spring.io/) もデフォが2.0.0になっています

# 開発
必要な設定やコードを書きます。
WebFlux + Thymeleafで動作確認をするための最小構成です。

## dependencies
（当たり前ですが）Spring Boot２系が必要

* spring-boot-starter-webflux
* spring-boot-starter-thymeleaf

```xml:pom.xml
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.0.0.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-thymeleaf</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-webflux</artifactId>
		</dependency>
	<dependencies>

```

## Controller
いつものthymeleafで利用するテンプレートの名前を返す処理に、Fluxをテンプレートに渡す処理を追加しましょう。

`Flux`についてはこちら ->  [23. WebFlux framework] (https://docs.spring.io/spring/docs/5.0.0.BUILD-SNAPSHOT/spring-framework-reference/html/web-reactive.html#web-reactive)

```java:WebController.java

@Controller
@RequestMapping("/test")
public class WebController {

    @GetMapping
    public String index(Model model) {
        Flux<String> flux = Flux
                .range(0, 5)
                .map(i -> "count :" + i)
                .repeat(10)
                .delayElements(Duration.ofSeconds(1L));

        model.addAttribute("items", new ReactiveDataDriverContextVariable(flux, 1));

        return "index";
    }
}
```

<details><summary>上記のFluxインスタンスの処理説明</summary><div>

|メソッド|概要|
|---|---|
|range|0から４までインクリメントして出力するFluxの生成|
|map|`count : 0`のような文字列を出力するFluxを返す|
|repeat|上記Fluxを10回繰り返す|
|delayElements|Fluxの出力毎のdelayを設定|

</div></details>

上記コードでやっていることは以下の通り
1. Flux<T>のインスタンスを生成
2. 生成したインスタンスを`ReactiveDataDriverContextVariable`でwrapしてModelに渡す

### ReactiveDataDriverContextVariable
新しいThymeleafのクラス
`org.thymeleaf.spring5.context.webflux.ReactiveDataDriverContextVariable;
`
このクラスでFluxインスタンスをwrapして`org.springframework.ui.Model`にaddするだけで**Data-Drivenモード**として動作する。
これにより、サーバ側で非同期的に出力したデータをSSE(Server Sent Event)で逐次的に表示側に送信される。
（ココらへん雰囲気的な理解度なので、理解が進んだら追記していきます。）

## template
ここは、ModelにListインスタンスが渡された時と同様に書いておけばOKです。
特別な記述は必要ありません。

```html
<html xmlns:th="http://www.w3.org/1999/xhtml">
<head>
</head>
<body>
<h1>FluxTest</h1>
<table>
    <thead>
    <tr>
        <th>title</th>
    </tr>
    </thead>

    <tbody>
    <tr th:each="item : ${items}">
        <td>[[${item}]]</td>
    </tr>
    </tbody>
</table>
</body>
</html>
```

# デモ
<img src="https://qiita-image-store.s3.amazonaws.com/0/39229/63690810-f679-4b4a-6115-e6b41943a590.gif" width=300 height=330>
※最初に幾つか表示されてしまっているのは、ただの録画ミスです(´；ω；｀)ﾌﾞﾜｯ
これではじめてのWebFlux + Thymeleafができました！

# 調査中にハマったところ

もともとプロトタイプ作成初期はSpring Boot 1系を利用していたときに
WebFluxを利用してみようと思い適当に依存を追加してコードを直したら以下のような表示になっていた。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/39229/d39bb8f0-e25b-970b-b80c-9607666497a3.png)

curlコマンドなどで、`content-type: text/event-stream`等を指定して単純なテキストを返したりする限りでは、しっかりとFluxの挙動をしていたため、最初はThymeleafの設定追加等を疑っていたが全く見つからず悩むこと数時間…

```
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-webflux</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
```
**spring-boot-starter-webと一緒に依存関係に追加**してしまっていたため
Thymeleafが以前までの解釈でテンプレを生成していたのではないかと。

通常のAPIとかは両方依存関係に存在しても問題なく動作しました／(^o^)＼

# まとめ
* Spring-Boot2が正式にリリースされました
* 使う機会があったりなかったりするかもしれませんが、入門するときの参考になれば
* `spring-boot-starter-webflux`と`spring-boot-starter-web`は一緒に定義するとThymeleafが正常に動作しない

SpringFramework5系 + SpringBoot2系をキャッチアップしておかねば(^o^

# 参考
* [danielfernandez/reactive-matchday](https://github.com/danielfernandez/reactive-matchday)
* [Spring I/O 2017 報告 ThymeleafのWebFlux対応](https://www.slideshare.net/TakuyaIwatsuka/spring-io2017-reportthymeleaf)