Spring Bootで画面を表示したいなら通常は `@Controller` とビュー名を、JSONなどのAPIレスポンスを返したいなら `@RestController` を使います。決め手はメソッドの戻り値の型だけでなく、返り値をビュー名として解釈するか、レスポンス本文へ書き込むかです。
結論:画面は@Controller、API本文は@RestControllerが基本
Spring MVCの `@RestController` は `@Controller` と `@ResponseBody` の働きを組み合わせたアノテーションです。クラス内の各ハンドラーメソッドの戻り値を、通常はHTTPレスポンス本文として扱います。
| 観点 | @Controller | @RestController |
|---|---|---|
| 主な用途 | Thymeleafなどの画面表示 | JSON・テキストなどのHTTP API |
| Stringを返す一般例 | ビュー名として解決 | 文字列を本文として返す |
| DTOなどのオブジェクト | @ResponseBodyなどがなければ通常のAPI本文にはならない | 変換器が対応すればJSONなどへシリアライズ |
| @ResponseBody | 必要なメソッドに付けられる | クラス全体に適用された状態 |
どちらでも `@GetMapping` や `@PostMapping` を利用できます。注目すべきなのはURLではなく戻り値の扱いです。
実例:@ControllerでThymeleafの画面を返す
たとえば `/products` にアクセスして一覧画面を表示したい場合、ビュー名 `products/list` を返す構成にできます。Thymeleafなどのビュー解決設定と、対応テンプレートの配置が必要です。
import java.util.List;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class ProductPageController {
@GetMapping("/products")
public String list(Model model) {
model.addAttribute("products", List.of("A", "B"));
return "products/list";
}
}
一般的なThymeleaf構成では `src/main/resources/templates/products/list.html` を用意します。ファイル名やViewResolverの設定が合わないと、ビューが見つからず画面表示に失敗します。
(Spring MVC全体のController・View・Modelの役割については『MVC構成を理解すれば現場で詰まらない』をご参照ください)
実例:@RestControllerでJSON APIを返す
画面ではなくデータを返すAPIは、戻り値を本文に書く `@RestController` が自然です。次の例はJava 17以降のrecordを使用し、JSON用のメッセージ変換器が設定されている前提です。
import java.util.List;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ProductApiController {
@GetMapping("/api/products")
public List<ProductDto> list() {
return List.of(new ProductDto("A"), new ProductDto("B"));
}
}
record ProductDto(String name) {}
JSON変換が利用できる構成なら、たとえば `[ {"name":"A"}, {"name":"B"} ]` に相当する配列を返します。`@RestController` という名前だけで、すべての戻り値がJSONになるわけではありません。`String` は文字列本文として扱われる点に注意してください。
@ResponseBodyは何を切り替えているのか
用語解説:@ResponseBody
メソッドの戻り値をViewResolverへ渡す代わりに、HTTPレスポンス本文へ書き込むことを指示するSpring MVCのアノテーションです。
画面と短いデータ応答を同じControllerに置く場合、必要なメソッドだけに `@ResponseBody` を付ける方法もあります。ただし用途が増えるほど役割を分けたほうがレビューしやすくなります。
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class PingController {
@GetMapping("/ping")
@ResponseBody
public String ping() {
return "pong";
}
}
この例の `pong` はビュー名ではなく本文です。一方、画面を表示する通常の `@Controller` メソッドで `return "products/list";` と書くと、ビュー名として解決されます。
画面が出ず文字列が表示されるときの切り分け
| 症状 | 最初の確認 | 考えられる修正 |
|---|---|---|
| products/listという文字列が出る | クラスに@RestControllerが付いていないか | 画面用途なら@Controllerとビュー名にする |
| ビューが見つからない/エラーページ | テンプレートの配置とビュー解決設定 | テンプレート名・依存関係を確認する |
| JSONのつもりがView名として扱われる | @Controllerで@ResponseBodyがないか | @RestControllerか@ResponseBodyを使用する |
| APIが期待のJSONにならない | 戻り値の型とHttpMessageConverter | 必要なJSON変換器と応答形式を確認する |
表示結果だけを見てアノテーションを書き換えると、別の画面やAPIに影響する場合があります。メソッド単位の責務と戻り値の型を合わせて確認してください。
(テンプレートが見つからない場合の原因調査については『Spring Bootアプリ開発で頻発するWhitelabel Error Page』をご参照ください)
APIでもステータスとヘッダーを制御したい場合
本文の返却方法とHTTPステータスの指定は別の関心事です。成功・未検出・入力エラーなどを明示的に扱うAPIでは、`ResponseEntity` や例外ハンドラーの利用も検討できます。
ただし、`@RestController` を付けたら入力チェックや例外処理まで自動で済むわけではありません。レスポンス本文の扱いと業務ロジックの責務を切り分けましょう。
(Spring Bootのアノテーション全体を確認したい場合については『Spring Bootのよく使われるアノテーションとは?』をご参照ください)
判断するときのチェックポイント
- 画面テンプレートを返すのか、データを返すのかを先に決める。
- `String` の意味をクラスとメソッドのアノテーション込みで確認する。
- ビュー名・テンプレートパス・Thymeleaf依存設定を一致させる。
- JSON APIではDTOなどの返却型とメッセージ変換器を確認する。
- 同一クラスで両方扱うなら、読み手が挙動を追いやすいか見直す。
戻り値をひとつ変える前に、どの層が「画面かデータか」を決めているか確認する。それが遠回りに見えて、原因の切り分けを速くします。
よくある質問
@ControllerからJSONを返すことはできますか?
はい。対象メソッドに@ResponseBodyを付けるなど、レスポンス本文として扱う指定をすれば可能です。@Controllerは「JSON不可」を意味しません。
@RestControllerでStringを返すとJSONになりますか?
一般にはStringはメッセージ変換器により文字列の本文として返されます。JSONオブジェクトを返したいならDTOなどの型とJSON変換器を確認してください。
@RestControllerでThymeleafのビュー名を返せますか?
通常、戻り値の文字列はビュー名ではなくレスポンス本文として処理されます。画面表示を目的とするなら@Controllerを検討してください。
まず既存のControllerを1クラス選び、アノテーション・戻り値・期待するHTTPレスポンスを順に照合してみてください。