【Train with Spring】2. 画面遷移とパラメータの受け取り

# 初めに
この記事は主に同期に対してSpringを紹介するために作成されました。
なるべく楽をしながらSpringと仲良くなるのが目的ですので、網羅的な説明は極力避け、目的を達成する手段が幾つかある中から、その内の1つを学ぶ形式で進みます。
また、記事中には正確性に欠ける表現が出現しますが、これも上記と同じ理由です。

## 前回までのあらすじ
- [【Train with Spring】1. プロジェクトのセットアップ](https://qiita.com/kthksgy/items/c8ac96ffe85b38feaa16)

前回はプロジェクトのセットアップを行いました。
これ以降は初回に作ったプロジェクトを基準にサンプルを掲載していきます。

# ページ遷移
では、Servlet・JSPでも最初に行うであろうページ遷移をSpringで実現しましょう！

## ソフトウェアアーキテクチャ
Servlet・JSPではMVCと呼ばれるソフトウェアアーキテクチャに基づいてプログラミングを行いますが、Springでもそれは変わりません。
ソフトウェアアーキテクチャとは、ソフトウェアの開発生産性(プログラミングの捗り具合)と保守性(後で変更を加える時のやりやすさ)を向上させるための設計方針です。

### Model-View-Controller (MVC)
簡単に内容を復習します。

Model-View-Controller(MVC, モデル・ビュー・コントローラ)は、Webアプリのようなユーザーが操作するためのインターフェース(User Interface, UI)を持つソフトウェアに適用されるソフトウェアアーキテクチャの1つです。

Webアプリにおいては、プログラムをサーバー側の処理(Model)、ユーザーに表示されるページ(View)、ユーザーからのアクセスに応じた画面遷移(Controller)に分けて開発する事で、見通しの良いプログラムが完成します。

## コントローラー(Controller)
ユーザーからアクセスのあったURLを基に、適切な画面をユーザーに配信する部分です。

### Servlet・JSPの場合
まずは、Servlet・JSPでコントローラーを作ります。

Servlet・JSPの場合はまずコントローラーパッケージ(`controllers`)を作って、こんな感じで`HelloServlet.java`を作成するんじゃないでしょうか。

```java
package controllers;  // コントローラーパッケージ

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/hello")  // http://localhost:8080/hello にサーブレットをマッピング
public class HelloServlet extends HttpServlet {
	@Override
	protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 変数名AISATSUに値をセットしてJSPに渡す
        req.setAttribute("AISATSU", "Hello World");
        // hello.jspを表示する
        req.getRequestDispatcher("hello.jsp").forward(req, resp);
	}
}
```

### Springの場合
では、Springでも同じ物を作ります。

まず、Servlet・JSPと同様にコントローラーパッケージ(`controllers`)を作成して、その中に`HelloController.java`を以下のように定義します。

```java
package com.example.trainwithspring.controllers;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller  // このクラスがコントローラーである事を明示
public class HelloController {
	@RequestMapping("/hello")  // http://localhost:8080/hello にこのメソッドをマッピング
	public String hello(Model model) {
        // 変数名AISATSUに値をセットしてThymeleafに渡す
		model.addAttribute("AISATSU", "Hello World");
        // src/main/resources/hello.htmlを表示する
		return "hello";
	}
}
```

恐らく、Servletが分かっていれば上のクラスは仮にコメントが無かったとしても大半を理解する事が出来るはずです。
Springでは`〇〇Servlet`というクラス単位ではなく、`〇〇Controller`のメソッド単位でURLにマッピングを行います。

## ビュー(View)
コントローラーから画面配信を行う際に、ユーザーにどのような画面を表示するかの部分です。

### Servlet・JSPの場合
ServletではJSP(JavaServer Pages)を用いて、Javaコードを含むHTMLを作成し、テンプレートにコントローラーから受け取ったデータを埋め込む事で実際の画面を作ります。

では、テンプレートファイルとして`src/main/webapp/WEB-INF/hello.jsp`を次のように作成します。

```jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>ハローワールド</title>
</head>
<body>
    <h1>${AISATSU}</h1>
</body>
</html>
```

このように書いて先ほどのコントローラーと組み合わせると、真っ白な画面に`Hello World`と表示されるようになります。

### Springの場合
Springでも、テンプレートにデータを埋め込んで画面を作るという流れは変わりません。

が、SpringではJSPではなくThymeleafというテンプレートエンジンを用います。
ThymeleafはJSPに比べ、よりHTMLに近い記法でテンプレートを作成可能です。

では、Thymeleafのテンプレートファイルとして`src/main/resources/hello.html`を以下のように定義します。

```html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>ハローワールド</title>
</head>
<body>
    <h1>[[${AISATSU}]]</h1>
</body>
</html>
```

簡単なテンプレートなので特に説明は不要だと思います。
記法の違いはありますが、ThymeleafでもJSPと同様にコントローラーから受け取ったデータをテンプレートに埋め込む事で画面を作ります。

# パラメータを受け取る
続いて、Webアプリの基本的な処理であるユーザーからのパラメータの受け取りについて学びます。

以下の簡単なログインフォームから送られて来たデータを、ServletとSpringでそれぞれ扱う事を考えましょう！

```
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>ログイン</title>
</head>
<body>
	<form action="/login" method="POST">
		<p>ユーザー名</p>
		<p>
			<input type="text" name="username">
		</p>
		<p>パスワード</p>
		<p class="pass">
			<input type="password" name="password">
		</p>
		<p>
			<input type="submit" value="ログイン">
		</p>
	</form>
</body>
</html>
```

## Servletの場合
では、Servletの場合からまず見ていきます。データを受け取りたいだけなのでそれ以外の部分は省略します。

```java
package controllers;

import java.io.IOException;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import entity.Employee;
import model.SelectAction;

@WebServlet("/login")  // このサーブレットを http://localhost:8080/login にマッピング
public class LoginServlet extends HttpServlet {
	@Override  // POSTの場合にこのメソッドが呼び出される
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // reqからusernameパラメータを取得
		String username = req.getParameter("username");
        // reqからpasswordパラメータを取得
		String password = req.getParameter("password");
        /* 省略 */
	}
}
```

## Springの場合
では、Springの場合を書きます。

```java
package com.example.trainwithspring.controllers;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

@Controller
public class LoginController {
	@PostMapping("/login")  // このメソッドを http://localhost:8080/login のPOSTにマッピング
	public String login(
			@RequestParam(name = "username", required = false) String username,
			@RequestParam(name = "password", required = false) String password) {
        // 引数に@RequestParamでアノテーションを付けるとパラメータが自動的に変数に代入される
		/* 省略 */
	}
}
```

このように、Springではパラメータを受け取る際にはメソッドの引数に`@RequestParam`アノテーションを付ける事で、アノテーションで指定したパラメータを引数に自動的に代入します。

他にも、セッションパラメータを受け取るための`@SessionAttribute`やクッキーを受け取るための`@CookieValue`も存在します。

しかも、SpringではこれらのパラメータをString以外で受け取る事も出来ます！

```java
// 新規登録
public String register(
			@RequestParam("username") String username,
			@RequestParam("password") String password,
            // 年齢だけInteger型に自動変換する事が出来る
            @RequestParam("age") Integer age) {
        // 引数に@RequestParamでアノテーションを付けるとパラメータが自動的に変数に代入される
		/* 省略 */
}

// セッションパラメータ"user"のデータを基にUserクラスのインスタンスを自動生成
public String userMyPage(@SessionAttribute User user) {
    /* 省略 */
}
```

### SpringでServletのように書く
実はSpringの内部ではServletが動作しており、コントローラーに限った範囲で言うとSpringはServletを扱いやすくする役割に過ぎないため、Servletのようなデータの受け取り方が可能です。

```java
package com.example.trainwithspring.controllers;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

@Controller
public class LoginController {
	@PostMapping("/login")  // 引数としてServletの時と同じ物を受け取る
	public String login(HttpServletRequest req, HttpServletResponse resp) {
        // reqからServletの時のようにusernameパラメータを取得
		String username = req.getParameter("username");
        // reqからServletの時のようにpasswordパラメータを取得
		String password = req.getParameter("password");
		/* 省略 */
	}
}
```

# まとめ
今回は簡単な画面遷移と基礎的なデータの受け渡しについてまとめました。

次回はSpring Data JPAを用いたデータベースアクセスを行う予定です。