Spring Boot でフォームの値を取得して操作

### Spring Boot でフォームの値を取得して操作
(2015.10.31 Yosemite)

Spring Boot プロジェクトを作成するところから、 
フォームの値を取得して操作するまでを実行してみました。 

Java のウェブアプリケーション作成は、ほとんど素人なので、 
色々とおかしなところがあるかもしれません。

[Mac Book Air (MBA) の初期設定メモ書き Part2](http://qiita.com/NariseT/items/306f6e7149873462e64e) にて、 
JDK と IntelliJ IDEA が導入済みです。

---
#### プロジェクトの雛形の作成

- ___Gradle のインストール___

```
brew install gradle
```

- ___IntelliJ IDEA で Gradle プロジェクトの作成___

IntelliJ IDEA を起動 -> Create New Project -> Gradle -> Next -> 

```
GoupId: org.mygroup.webapp
ArtifactId: spring-boot-example1
Version: 0.0.1-SNAPSHOT
```

-> Next -> Next -> Next -> Finish

- ___build.gradle の編集___

以下のように編集

```build.gradle
group 'org.mygroup.webapp'
version '0.0.1-SNAPSHOT'

buildscript {
    ext {
        springBootVersion = '1.2.7.RELEASE'
    }
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
        classpath('io.spring.gradle:dependency-management-plugin:0.5.2.RELEASE')
        classpath('org.springframework:springloaded:1.2.1.RELEASE')
    }
}

apply plugin: 'java'
apply plugin: 'idea'
apply plugin: 'spring-boot'
apply plugin: 'io.spring.dependency-management'

jar {
    baseName = 'spring-boot-example1'
    version = '0.0.1-SNAPSHOT'
}

repositories {
    mavenCentral()
}

dependencies {
    compile('org.springframework.boot:spring-boot-starter-web')
    compile('org.springframework.boot:spring-boot-starter-thymeleaf')
    testCompile('org.springframework.boot:spring-boot-starter-test')
}

task wrapper(type: Wrapper) {
    gradleVersion = '2.8'
}
```

- ___リポジトリのアップデートと、build.gradle のリフレッシュ___ 

初めてだと、IntelliJ IDEA の出すメッセージに従って、 
リポジトリのアップデートと、build.gradle のリフレッシュが出来ましたが、 
いつでもできるように方法を調べました。

- ___リポジトリのアップデート___

IntelliJ IDEA メニュー -> Preferences (⌘,) 
-> Build, Execution, Development 
-> Build Tools -> Maven -> Repositories 
-> URL: http://repo1.maven.org/maven2 を選択 
-> Update ボタンをクリック

- ___build.gradle のリフレッシュ___

IntelliJ IDEA ウィンドウの右端中央付近にある "Gradleアイコン" をクリック 
（無いときは、左下端にある "ウィンドウの絵のようなアイコン" をクリック） 

-> "Gradleアイコン" を押すと、Gradle Tool Window が開く 
-> Gradle Tool Window 左上端にある水色のリフレッシュボタンを押す

- ___雛形の作成___

http://start.spring.io/ にアクセス 
-> Gradle プロジェクト、Spring Boot 1.2.7 を選択 
-> フォームを以下のように入力

```
Group: org.mygroup.webapp 
Artifact: spring-boot-example1
```

-> Generate Project ボタンをクリック 
-> ダウンロードされた zip ファイルを解凍 
-> 中にある src フォルダを IntelliJ IDEA で作成したプロジェクトルートに移動

プロジェクトのディレクトリ構造

```
spring-boot-example1
├── build.gradle
├── gradle
│   └── wrapper
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── settings.gradle
├── spring-boot-example1.iml
└── src
    ├── main
    │   ├── java
    │   │   └── org
    │   │       └── mygroup
    │   │           └── webapp
    │   │               └── SpringBootExample1Application.java
    │   └── resources
    │       └── application.properties
    └── test
        └── java
            └── org
                └── mygroup
                    └── webapp
                        └── SpringBootExample1ApplicationTests.java
```

---
#### HTML File の作成 

[Handling Form Submission](https://spring.io/guides/gs/handling-form-submission/) を参考に、 
フォームから値を受け取って、表示するサイトを作成

- ___index.html___ 

/src/main/resources/templates フォルダを作成 
-> そこに、まずは index という名前の HTML File を作成 
（index.html は、フォームに入力された値を送信する役割）

```index.html
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <title>Index</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<h1>Form</h1>
<form action="#" th:action="@{/result}" th:object="${indexForm}" 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>
```

`th:action="@{/result}"` がフォームのアクション指定 
`th:object="${indexForm}"` がフォームの内容が全て入る indexForm オブジェクト 
`th:field="*{id}"` が indexForm オブジェクトの id フィールド 
`th:field="*{content}"` が indexForm オブジェクトの content フィールド

Submit ボタンが押されると、"/result" にマッピングされたメソッドが呼ばれる。 
そのメソッドは引数として、indexForm オブジェクトを受け取ることができる。 

- ___result.html___ 

続いて、/src/main/resources/templates に result という HTML File を作成 
（result.html は、フォームに入力された値を表示する役割） 

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

`${indexForm.id}` で、送られてきた indexForm オブジェクトの id フィールドを取得 
`${indexForm.content}` で、送られてきた indexForm オブジェクトの content フィールドを取得 
Submit another message リンクで、"/" にマッピングされたメソッドが呼ばれる 

---
#### Java Class の作成
 
- ___SpringBootExample1Application.java___ 

org.mygroup.webapp パッケージには、以下のクラスが自動生成済み。 
このクラスには、特に変更を加える必要はありませんでした。

```SpringBootExample1Application.java
package org.mygroup.webapp;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SpringBootExample1Application {

    public static void main(String[] args) {
        SpringApplication.run(SpringBootExample1Application.class, args);
    }
}
```

- ___IndexFormController.java___ 

同じ org.mygroup.webapp パッケージに 
IndexFormController という Java Class を作成。 

"/" でも、"/index" でも index.html が表示されるように、 
どちらでも `return "index";` としました。

複数の URL を、一つの html ファイルにマッピングするのに、 
何かもっとちゃんとした書き方がありそうな気はします・・・

(2015.11.16 追記)
[Multiple Spring @RequestMapping annotations](http://stackoverflow.com/questions/2513031/multiple-spring-requestmapping-annotations) によると、
`@RequestMapping(value={"/", "/index"})` とすれば良かったみたいです

```IndexFormController.java
package org.mygroup.webapp;

import org.mygroup.webapp.model.IndexForm;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
public class IndexFormController {

    @RequestMapping(value="/")
    public String index1(Model model) {
        model.addAttribute("indexForm", new IndexForm());
        return "index";
    }
    @RequestMapping(value="/index")
    public String index2(Model model) {
        model.addAttribute("indexForm", new IndexForm());
        return "index";
    }

    @RequestMapping(value="/result", method=RequestMethod.POST)
    public String indexFormSubmit(@ModelAttribute IndexForm indexForm, Model model) {
        if (indexForm.getId() == 1) {
            indexForm.setContent("お前がナンバーワンだ！");
        }
        model.addAttribute("indexForm", indexForm);
        return "result";
    }
}
```

`model.addAttribute("indexForm", new IndexForm())` として、 
空のインスタンスをフォームにセットしないと、index.html を開くとエラーが発生。 

```
Error during execution of processor 'org.thymeleaf.spring4.processor.attr.SpringInputGeneralFieldAttrProcessor' (index:10)
```


`@RequestMapping(value="/result", method=RequestMethod.POST)` で、 
"/result" とindexFormSubmit というメソッドをマッピング（メソッドが POST のときだけ）。

`indexFormSubmit(@ModelAttribute IndexForm indexForm ...` で、 
次に作成する IndexForm オブジェクトの indexForm インスタンスとしてフォームの値を取得。

- ___IndexForm.java___ 
 
org.mygroup.webapp.model というパッケージを作成して、そこに、 
IndexForm という Java Class を作成。 
フォームから送られてきた indexForm の内容を格納するクラス。

```IndexForm.java
package org.mygroup.webapp.model;

public class IndexForm {

    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;
    }
}
```

---
#### gradle bootRun

IntelliJ IDEA の Terminal から、`gradle bootRun` を実行。 
適当なブラウザで `localhost:8080` としてサイトに接続し、動作確認。

---
#### その他

/src/main/resources/application.properties にて、 
ポートの変更や、ホットデプロイのための設定が出来る。

```
server.port=8080
spring.thymeleaf.cache=false
```

---
#### GitHub に投稿

[Mac Book Air (MBA) の初期設定メモ書き Part2](http://qiita.com/NariseT/items/306f6e7149873462e64e) にて、 
GitHub に接続できる状態になっています。

[GitHub](https://github.com/) にて、spring-boot-example1 というリポジトリを作成 
-> プロジェクトルートにて `vim .gitignore` として、以下のように編集

```.gitignore
.DS_Store
.gradle
.idea
build*
gradle*
*.iml
settings*
```

続いて、以下を実行 

```
git init
git add .
git commit -m 'spring boot example1: handling form submission'
git remote add origin github:/username/spring-boot-example1.git
git push -u origin master
```