前回は、Webアプリケーションの基本的な構造である「MVCパターン」について学びました。

MVCパターンは、Model(データ管理)、View(画面表示)、Controller(制御)の3つの要素で構成されており、それぞれの役割を明確に分けることで、保守性の高いアプリケーションを作ることができます。

今回は、その中でもコントローラーの役割を担うSpring BootのControllerについて詳しく学んでいきます。Controllerを理解することで、リクエストを受け取り、適切な処理を行い、レスポンスを返す流れを実装できるようになります。

今回の学習の重点をオレンジの枠線で図示します。

主としてコントローラーについて学びます

1. Controllerとは?

1.1 Spring BootのController

Spring BootのControllerとは、ユーザからのリクエストを受け取り、必要に応じてビジネスロジック(Model)を呼び出し、結果をView(Thymeleafなど)に返すクラスのことです。

Spring Bootでは@Controllerアノテーションを付けたクラスがその役割を担います。

上図のようにブラウザからアプリケーション(Spring Boot)がリクエストを受けると、該当のControllerメソッドが呼ばれ、必要に応じてModelを操作してデータを取得・加工します。

そして、そのデータをThymeleafテンプレートに渡してHTMLを生成し、ブラウザへ返します。

1.2 ControllerでHello World

まずは、Spring Bootでの「Hello World」をやってみましょう。

以下のようなControllerクラス(例:Hello1Controller.java)を作成します。

あらかじめSpring Bootプロジェクトを作成しておいてください(前章参照)。

package com.example.demo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class Hello1Controller {

    @GetMapping("/hello1")
    public String helloWorld() {
        System.out.println("Hello World");
        return "blank"; 
        // blank.html(Thymeleafファイル)は作成していません
    }
}

  1. @Controller アノテーションによって、このクラスはHTTPリクエストを処理するControllerであるとSpringに認識されます。
  2. @GetMapping("/hello1") は、http://localhost:8080/hello1 にGETリクエストが来たときに呼び出されるメソッドです。
  3. System.out.println("Hello World"); はログをコンソールに出力しています。ブラウザには表示されません。

実行してブラウザで/hello1にアクセスすると、Thymeleafテンプレートblank.htmlが返ってくる想定です(今回は作成していません)。

Eclipseでは、コンソールビューがあり、System.out.printlnなどのログはそちらに表示されます。プリントデバッグや各種エラー出力を行う場所として頻繁に目にすることになります。

このときのアドレスバーの表示が「http://localhost:8080/hello1」となっていて、「http://localhost:8080/blank.html」ではないことに注目して下さい。「templates」フォルダのファイル名は表示されないのです。

アノテーションは『型の一種』であるため、利用するときにはパッケージのインポートが必要

アノテーションは厳密には「クラス」ではありません。Javaにおけるアノテーション(annotation)は、特別な型である「インターフェースの一種(正確には『インターフェースを拡張した特殊な型』)」です。

「templates」フォルダのファイル名は表示されない理由はセキュリティ

templates フォルダは、Thymeleaf などのテンプレートエンジンによって処理されるビュー専用のディレクトリ であり、静的リソース(過去に学んだHTML)とは異なります。もし外部から直接アクセスできると、未加工のテンプレートファイルがそのまま閲覧され、機密情報が漏洩する可能性があるわけです。

Spring Bootのデフォルト設定では、templates フォルダ内のファイルをブラウザ経由で直接表示することはできず、必ず Controller を通じて 処理されることになっています。

1.3 @GetMapping()とは?

Spring Bootでは@GetMapping("/hello1")を使います。これは「このControllerメソッドは /hello1 というURLパスで呼び出せます」という意味です。パスとメソッドを対応させることからマッピングということもあります。

Spring BootアプリケーションのURLは http://ドメイン名:ポート番号/パス となります。

複数のControllerが同じURLパスを持っていると競合してしまい、正常に動作しない可能性があります。同じURLパスは一意にしておくのが基本ルールです。

ITの世界でよく使われるmapという言葉

ITの世界では1対1の対応付けるものをmapと呼びます。英語の【map(地図)】にも「現実の地形を地図上の図形に対応させるもの」 という意味があります。

マッピングとパッケージ構造は無関係

Spring Bootのマッピング( @GetMappingや@PostMapping)とパッケージ構造は、直接の関係はありません。
Spring Bootでは、マッピング(URLのパス)はあくまでアノテーションで指定された内容が基準になるため、Javaのパッケージ構成がURLに自動的に影響を与えることはありません。

実験

同じ@GetMapping("/hello1")を持つメソッドが複数存在すると競合を起こすので試してみて下さい。

試した後は競合状態を解消してEclipseを再起動して下さい。

2. Controllerクラスでデータを受け取る

@Controller アノテーション付きクラスと、メソッドに@GetMapping@PostMappingを付けることでHTTPのGET/POSTを受け取ることができます。

2.1 @GetMappingメソッド

GETリクエストを処理したい場合は、@GetMappingを付けたメソッドを用意します。

@GetMapping("/hello2")
public String hello2() {
    // GETリクエストを受け取った際の処理
    return "viewname";
}

もしPOSTリクエストを処理したい場合は、@PostMappingを付けたメソッドを用意します。(5章で解説)

実験

@PostMapping("/hello2") のみを持つControllerメソッドしか用意しなかった場合に、ブラウザで /hello2 にアクセスしたらどうなるでしょうか?
→ ブラウザの通常アクセスはGETリクエストなので、「Method 'GET' is not supported.」という405エラーになるはずです。

2.2 Modelクラスとは?

Spring Boot(Thymeleaf)でコントローラからViewへデータを渡すには、Modelという仕組みを使います。Model は Controller から View にデータを渡すためのオブジェクトであり、キーとバリューのペアでデータを保持します。

Modelを使ってコントローラからViewへデータを渡す

注)以下はimport文を使わずにModelクラスを引数にしています。

@GetMapping("/hello2")
public String hello2(org.springframework.ui.Model model) {
    model.addAttribute("message", "Hello World");
    return "result"; // result.htmlへ渡す
}

  • model.addAttribute("message", "Hello World");で「message」という名前で "Hello World" をテンプレートに渡す。
  • return "result"; で指定したテンプレート(result.html)から message を参照できます。

このModelはMVCパターンのMとは別物です

このModelは、MVCパターンにおける「Model層(ビジネスロジックやデータの管理を行う層)」そのものではありません。 あくまでもControllerとView間でデータをやり取りするための一時的なコンテナ、データの入れ物です。

調べてみましょう

実は Model はクラスではなく インタフェース ですが、Springが 適切な実装クラスを裏側で提供している ため、あたかも「実体化されている」ように見えます。Springの依存性注入(DI:Dependency Injection)の仕組みにより、開発者が明示的に new しなくても Model を使用できるようになっています。

余裕があれば依存性注入について調べてみてください。

スコープ(有効範囲)

model.addAttribute() でセットしたデータは、そのリクエストでViewを表示するタイミングまで有効です。レスポンスが返却された後は破棄されるため、「使い捨て」の領域です。この点が5章で学ぶセッションとの違いです。

3. Thymeleafにおける記述方法

属性の取り出し方

Thymeleafでは、以下のように${変数名}を書くと、Modelに設定された属性を取り出すことができます。

<p th:text="${message}">ここにメッセージが入ります</p>

Controllerで model.addAttribute("message","Hello World") とセットした値がHTMLの「ここにメッセージが入ります」と入れ替わり表示されます。

Modelを使ったデータの受け渡し

例:

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
  <meta charset="UTF-8">
  <title>Result</title>
</head>
<body>
  <h1>結果表示</h1>
  <p th:text="${message}">ここにメッセージが入ります</p>
</body>
</html>

Spring Bootでは「Controller」→「Thymeleafテンプレートに戻り値」としてデータを渡すのが基本のデータの流れです。

単体でHTMLを開いても、messageなどは埋め込まれず、「ここにメッセージが入ります」と表示されます。(必ずControllerを経由して値をセットしてあげる必要があります。)

4. 常にControllerを経由してThymeleafにアクセスさせる

繰り返しになりますが、直接Thymeleafテンプレート(.html)にアクセスするのは推奨されません。その理由は以下の3点です。

  1. ビジネスロジックが露出してしまう
    • コントローラーを通さず直接テンプレートにアクセスされると、意図しないデータを表示・操作される可能性があります。
  2. URL設計の問題
    • どのControllerから遷移したのか分からないため、パラメータの受け渡しや制御が難しくなります。
  3. セキュリティの問題
    • 認証・認可をバイパスして直接ページにアクセスされる恐れがあります。

必ずControllerを経由し、Controller側でModelに値を設定し、正しいView名を返すようにしてください。

CSS・JavaScript・画像などの静的ファイルはstaticに置く

templates(動的コンテンツ)
src/main/resources/templates/ に配置し、Thymeleafなどのテンプレートエンジンを使って動的に生成されます。コントローラーからModelを渡し、データを埋め込んでHTMLを生成します。

static(静的コンテンツ)
src/main/resources/static/ に配置し、CSS・JavaScript・画像などの静的ファイルをそのまま提供します。「/css/style.css」のように直接アクセスできます。

慣例として@GetMapping("/")でルートパス (/) を用意する

理由は、Webアプリケーションでは、/ をトップページ(ホーム)にすることが一般的だからです。ただし、必須ではありません。

アプリの設計次第では @GetMapping("/") を定義せず、すべてのエンドポイントを /api/... のように分けることもあります。

練習問題

練習問題1(Controllerの基本理解)

次のようなControllerクラスがあります。

@Controller
public class MyController {
    
    @GetMapping("/test")
    public String testMethod() {
        System.out.println("テストメソッドが呼ばれました");
        return "sample";
    }
}

以下の問いに答えてください。

(1)ブラウザでhttp://localhost:8080/test にアクセスしたとき、ブラウザにはどのようなファイル名の内容が表示されるでしょうか?

(2)このとき、Eclipseのコンソールには何が表示されるでしょうか?

(3)以下の説明から正しいものを選びましょう(複数可)。

A)URLにhttp://localhost:8080/sample.htmlと表示される。

B)URLにhttp://localhost:8080/testと表示される。

C)templatesフォルダのHTMLファイルには直接アクセスできる。

D)templatesフォルダのHTMLファイルには直接アクセスできない。


練習問題2(リクエストマッピングの理解)

以下のように複数のメソッドを持つControllerクラスを作成しました。

@Controller
public class SampleController {
    
    @GetMapping("/sample")
    public String getSample() {
        return "sample";
    }
    
    @PostMapping("/sample")
    public String postSample() {
        return "samplePost";
    }
}

(1)ブラウザからhttp://localhost:8080/sample にアクセス(GET)した場合、表示されるテンプレート(HTMLファイル)は何でしょうか?

(2)ブラウザから通常のリンクをクリックした場合のリクエストはGET、POSTのどちらですか?

(3)上記Controllerに以下のメソッドを追加するとどうなるでしょうか?

@GetMapping("/sample")
public String anotherGetSample() {
    return "anotherSample";
}

理由も合わせて説明してください。


練習問題3(ModelとThymeleaf連携)

以下のControllerのコードを参考にして質問に答えてください。

@Controller
public class MessageController {

    @GetMapping("/message")
    public String messageMethod(Model model) {
        model.addAttribute("greeting", "こんにちは、Spring Boot!");
        return "messageView";
    }
}

テンプレート(messageView.html)の一部:

<body>
  <h1>メッセージ表示</h1>
  <p th:text="${greeting}">ここにメッセージが表示されます</p>
</body>

(1)ブラウザでhttp://localhost:8080/messageにアクセスすると、画面には何が表示されるでしょうか?

(2)テンプレートファイルをブラウザで直接開いた場合(ファイルを右クリック>「次で開く」>「Webブラウザ」などで開いた場合)、画面にはどのように表示されるでしょうか?理由も説明してください。

(3)model.addAttribute("greeting", "こんにちは、Spring Boot!")というコードは何をしているか、簡潔に説明してください。


<まとめ:隣の人に正しく説明できたらチェックを付けましょう>

□ Spring BootのControllerとは、Javaで書かれたサーバサイドプログラムである

□ Controllerはアプリケーションのメモリ上に常駐し、リクエストを受け取り、レスポンスを返す(細かいビジネスロジックはModelに任せる)

□ HTTPメソッドに応じて@GetMapping@PostMappingでメソッドを切り替えられる

□ コンソールでプリントデバッグでき、各種エラーも表示される

@GetMapping("/hello")などでURLパスとメソッドの対応を定義する(重複は避ける)

□ Spring BootアプリケーションのURLは http://ドメイン名:ポート番号/パス となる

□ ControllerからThymeleafに値を渡すにはModelを使用し、model.addAttribute("name", value) のように書く

□ HTMLテンプレート(View)を返すには、メソッドの戻り値にView名(例えば"result")を書くだけで「.html」は不要

□ ThymeleafでModelに追加されたデータを参照するには ${変数名} と書く

第3章の今回はControllerからThymeleafにデータを渡してWebに表示する流れについて学びました。

第4章は「フォームからデータを送る 〜 リクエストとレスポンスの仕組み」です。

コントローラーでデータを受け取ることができたら、次はHTML(Thymeleaf)を使いデータをコントローラーに送る部分を学びます。

HTML(Thymeleaf)を使いデータをコントローラーに送る