Spring Bootの@RequestMapping戻り値とは?画面遷移とJSON応答の仕組みを解説
新人
「Spring Bootでコントローラーを作っているのですが、メソッドの最後にある『return "index";』とかの戻り値って、結局どこで何を決定しているんですか?」
先輩
「それは非常に重要なポイントですね。その戻り値は、ブラウザに『次にどの画面を表示させるか』、あるいは『どのようなデータを直接返すか』を決定する司令塔の役割を持っているんですよ。」
新人
「文字列を返せば画面が開くし、設定によってはデータそのものが返ることもあると聞きました。その違いや仕組みを詳しく知りたいです!」
先輩
「わかりました。Webアプリケーションの基本となる画面遷移と、最近主流のJSONデータ応答の違い、そして裏側で動いているViewResolverの仕組みまで、順番に整理していきましょう。」
1. @RequestMappingの戻り値は何を決定するのか?
JavaのWeb開発フレームワークであるSpring Bootにおいて、@RequestMapping(アット・リクエストマッピング)は、ユーザーからのリクエスト(URLへのアクセス)とJavaのメソッドを結びつける「受付窓口」のような役割を果たします。
このメソッドが返す「戻り値(return値)」は、主に「次にユーザーに見せるもの」を決定します。具体的には、以下の2つのパターンに大別されます。
- HTMLテンプレート(画面)を表示する: 指定した名前のファイル(HTMLなど)を読み込んで、ブラウザに表示させます。
- データ(JSONなど)を直接返す: 画面そのものではなく、数値やテキスト、リストなどの「情報の塊」をレスポンスとして返します。
プログラミング未経験の方にとって、「戻り値」という言葉は難しく感じるかもしれません。これは、メソッドという「命令のセット」が実行し終わった後に、呼び出し元に手渡す「結果報告」のようなものだと考えてください。Webの世界では、その報告内容が「次はindex.htmlを表示して!」という指示書になったり、「これが検索結果のデータだよ!」という中身そのものになったりするのです。
この戻り値の型によって、Spring Bootが内部でどのように処理を分岐させるかが決まります。もっとも一般的なのは、文字列を返すString型や、データと画面情報をセットにするModelAndView型です。
2. 画面遷移(String/ModelAndView)とデータ応答(JSON)の違い
Webアプリケーションには、大きく分けて「ページが切り替わるタイプ」と「ページはそのままでデータだけが更新されるタイプ」の2種類があります。これらは戻り値の書き方で制御します。
画面遷移(String / ModelAndView)
従来のWebサイトのように、リンクをクリックして新しいページに移動する場合に利用します。
String型: メソッドの戻り値として「"home"」のような文字列を返すと、Springは「home.html」というファイルを探して表示します。非常にシンプルで、最もよく使われる手法です。
ModelAndView型: 「表示したい画面の名前」と「画面に渡したいデータ(ユーザー名など)」を一つのオブジェクトに詰め込んで返す方法です。少し古い書き方に見えますが、データと遷移先を明確にセットにできるメリットがあります。
@Controller
public class MyWebController {
@RequestMapping("/welcome")
public String welcomePage() {
// "welcome"という名前のView(画面)を呼び出す指示
return "welcome";
}
}
データ応答(JSON / @ResponseBody)
最近のスマートフォンアプリや、画面をリロードせずに情報を書き換えるWebサイト(Ajax通信など)では、HTMLファイルではなくJSON(ジェイソン)という形式のデータを返します。
JSONとは、{"name": "田中", "age": 25}のように、データが整理されたテキスト形式のことです。これを実現するには、メソッドに@ResponseBodyという注釈を付けるか、クラス全体に@RestControllerを付けます。
@RestController
public class MyDataController {
@RequestMapping("/api/user")
public User getUserData() {
User user = new User("田中", 25);
// オブジェクトを返すと、自動的にJSON形式に変換される
return user;
}
}
@ResponseBodyの有無で決まります。初心者がやりがちなミスとして、「画面を表示させたいのにデータとして文字列が出力されてしまった」というものがありますが、これは注釈の設定ミスが原因であることが多いです。
3. ViewResolver(ビュー解決)の仕組みと物理パスの生成
コントローラーがreturn "index";と指示を出した後、Spring Bootの裏側ではViewResolver(ビューリゾルバ)という機能が忙しく働いています。
ViewResolverは、コントローラーが返した「論理名(index)」を、サーバー上の実際のファイルがある「物理パス(/src/main/resources/templates/index.html)」に変換する橋渡し役です。
物理パス生成の流れ
- コントローラーが「"hello"」という文字列を返す。
- ViewResolverが設定を確認する。
- 設定されている接頭辞(prefix)と接尾辞(suffix)を「hello」にくっつける。
- 最終的なファイルの場所を特定し、その中身を読み取ってブラウザに送信する。
例えば、多くのSpring Bootプロジェクトでは、内部的に以下のような設定が自動的に行われています。
- 接頭辞(prefix):
/templates/ - 接尾辞(suffix):
.html
この場合、戻り値が「"shop/item"」であれば、実際のファイルは/templates/shop/item.htmlを指すことになります。このように、プログラム内では短い名前(論理名)だけを扱い、細かい保存場所はViewResolverに任せることで、ファイルの場所が変わってもプログラムを修正しなくて済むようになっています。
実際のHTML構成例(Thymeleaf使用時)
Spring Bootでよく使われるテンプレートエンジン「Thymeleaf(タイムリーフ)」を使った場合のHTMLファイルの配置イメージです。
<!-- src/main/resources/templates/welcome.html -->
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>ようこそ</title>
</head>
<body>
<h1>Spring Bootの世界へようこそ!</h1>
<p>これはコントローラーの戻り値によって表示された画面です。</p>
</body>
</html>
初心者のうちは、「returnした名前」と「ファイル名」が一致している必要がある、という点だけをしっかり覚えておけば大丈夫です。もし「404 Not Found(ファイルが見つからないエラー)」が出た場合は、このViewResolverが指定の場所にファイルを見つけられなかったことを意味します。
4. String型によるテンプレート名の返却とリダイレクト・フォワード
Spring Bootのコントローラーにおいて、戻り値にString型を採用することは、最も一般的で柔軟な方法です。この文字列は、単なるテキストとしてブラウザに表示されるわけではなく、前述したViewResolverによって「どのHTMLファイルを表示するか」という命令として解釈されます。
しかし、単に画面を表示するだけでなく、別のURLへ移動させたい(リダイレクト)場合や、内部で処理を引き継ぎたい(フォワード)場合もあります。これらは戻り値の文字列に特定の接頭辞を付けることで制御可能です。
リダイレクト(redirect:)
リダイレクトは、サーバーからブラウザに対して「別のURLにアクセスし直してください」と指示を出す仕組みです。ブラウザのアドレスバーのURLが書き換わり、新しいリクエストが発生します。主に、フォームの送信(POST処理)が完了した後に、二重送信を防止するために完了画面や一覧画面へ飛ばす「要因」として使われます。
フォワード(forward:)
フォワードは、サーバー内部で別の処理にバトンタッチする仕組みです。ブラウザには通知されないため、URLは変わりません。同一アプリケーション内での内部的な転送に利用されますが、Spring Bootの開発ではリダイレクトに比べると使用頻度は低めです。
@Controller
public class TransitionController {
// 通常の画面表示
@RequestMapping("/entry")
public String entryPage() {
return "registration/input"; // templates/registration/input.htmlを表示
}
// 処理実行後のリダイレクト
@RequestMapping("/register")
public String registerProcess() {
// 登録処理(DB保存など)をここで行うと仮定
System.out.println("登録処理が完了しました。");
// 完了画面のURLへリダイレクト
return "redirect:/complete";
}
// リダイレクト先のメソッド
@RequestMapping("/complete")
public String completePage() {
return "registration/finished";
}
}
このように、戻り値の文字列一つでWebアプリケーションの「流れ」を自由自在にコントロールできます。リダイレクトを使う際は、return "redirect:パス";のように、コロンの後に遷移先のURLパスを記述する点に注意しましょう。
5. @ResponseBody(ResponseEntity)を使用したJSONレスポンスの実装
現代のWebシステム、特にSPA(Single Page Application)やスマートフォンのネイティブアプリと連携する場合、サーバーはHTML画面ではなく「純粋なデータ」を返す必要があります。この際に用いられるのが、@ResponseBodyアノテーションやResponseEntity型です。
@ResponseBodyによる自動変換
メソッドに@ResponseBodyを付与すると、Spring Bootは「戻り値はViewの名前ではなく、レスポンスの本体(ボディ)そのものだ」と判断します。もし戻り値がJavaのオブジェクト(JavaBeans)であれば、Spring内部に組み込まれたライブラリ(Jacksonなど)が、自動的にそのオブジェクトをJSON形式の文字列に変換してくれます。
ResponseEntityで詳細な制御
より高度な制御が必要な場合は、ResponseEntity<T>を使用します。これは、データ本体だけでなく、HTTPステータスコード(200 OKや404 Not Foundなど)や、レスポンスヘッダーを細かくカスタマイズして返すためのクラスです。
@RestController // クラス内の全メソッドに@ResponseBodyが適用される
@RequestMapping("/api/products")
public class ProductApiController {
@RequestMapping("/{id}")
public ResponseEntity&lt;Product&gt; getProduct(@PathVariable int id) {
// 本来はサービス層からデータを取得する
Product product = new Product(id, "高性能ノートPC", 150000);
if (product != null) {
// ステータスコード200 OKでデータを返す
return ResponseEntity.ok(product);
} else {
// データがない場合は404 Not Foundを返す
return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
}
}
}
// データの入れ物(DTO)
class Product {
private int id;
private String name;
private int price;
public Product(int id, String name, int price) {
this.id = id;
this.name = name;
this.price = price;
}
// Getter, Setterは省略
}
上記のコードを実行し、ブラウザやAPIクライアントからアクセスすると、以下のようなJSONレスポンスが得られます。
{
"id": 1,
"name": "高性能ノートPC",
"price": 150000
}
このように、戻り値をResponseEntityで包むことで、クライアントに対して「処理が成功したのか」「エラーだったのか」をより正確に伝えることが可能になります。これはWeb API設計におけるベストプラクティスの一つです。
6. Modelオブジェクトを利用したViewへのデータ渡しの基本
画面遷移を伴うWebアプリケーションにおいて、最も重要なのは「Java側で用意したデータを、いかにしてHTML(View)側に表示させるか」という点です。これを実現するために、Spring BootではModelオブジェクトという仕組みを提供しています。
Modelは、いわば「JavaとHTMLの間を行き来する、データ専用の運び屋(マップ)」のような存在です。コントローラーのメソッドの引数にModelを定義するだけで、Springが自動的に準備してくれます。
データの追加方法
使い方は非常にシンプルで、model.addAttribute("属性名", 値);という形式でデータを格納します。ここで指定した「属性名」をキーワードにして、HTMLテンプレート(Thymeleafなど)側で値を取り出すことになります。
@Controller
public class UserProfileController {
@RequestMapping("/profile")
public String showProfile(Model model) {
// 画面に渡したいデータを用意
String userName = "山田 太郎";
int points = 1500;
List&lt;String&gt; roles = Arrays.asList("一般ユーザー", "プレミアム会員");
// Modelにデータを詰め込む
model.addAttribute("name", userName);
model.addAttribute("userPoints", points);
model.addAttribute("userRoles", roles);
// templates/profile.htmlを表示
return "profile";
}
}
HTML(Thymeleaf)側での受け取り
Modelに格納されたデータは、HTML側でThymeleafの特殊な属性(th:textなど)を使って表示します。
<!-- templates/profile.html -->
<div class="container mt-4">
<h2>ユーザープロフィール</h2>
<p>お名前: <span th:text="{name}">名無し</span> さん</p>
<p>保有ポイント: <span th:text="{userPoints}">0</span> pt</p>
<h3>所属グループ</h3>
<ul>
<li th:each="role : ${userRoles}" th:text="${role}">役割</li>
</ul>
</div>
ここで重要なのは、Java側でmodel.addAttribute("name", ...)としたからこそ、HTML側で${name}として参照できるという対応関係です。もし名前が一致していないと、画面には何も表示されないか、エラーの原因となります。
また、Modelには文字列や数値だけでなく、自作したクラスのインスタンスや、リスト(List)、マップ(Map)など、あらゆるJavaオブジェクトを渡すことができます。大規模な開発では、データベースから取得したエンティティのリストをModelに詰め込み、それをHTMLのテーブル形式で表示する、といった処理が頻繁に行われます。
このように、コントローラーの戻り値が「行き先(View)」を決め、Modelが「荷物(データ)」を運ぶという役割分担を理解することが、Spring Boot習得の大きな一歩となります。
7. 戻り値の型を使い分けるための設計判断基準
Spring Bootの開発において、コントローラーの戻り値にどの型を採用するかは、アプリケーションの設計思想や「誰がそのデータを利用するのか」によって決定されます。適切な型を選択することで、コードの可読性が向上し、メンテナンスのしやすいシステムを構築することができます。
フロントエンドの技術スタックによる選択
最も大きな判断基準は、画面表示の仕組みです。ThymeleafやJSPといったサーバーサイドのテンプレートエンジンを使用している場合は、String型やModelAndView型を選択するのが標準的です。これにより、サーバー側でHTMLを生成してブラウザに送信する「サーバーサイドレンダリング」が実現します。
一方で、ReactやVue.js、Angularといったモダンなフロントエンドフレームワークを併用する場合や、スマートフォンのネイティブアプリ向けに機能を提供する場合、サーバーは「画面の枠組み」を返す必要はありません。この場合は、@RestControllerやResponseEntity型を使用して、純粋なJSONデータのみを返却する設計(APIサーバーとしての役割)に特化させます。
柔軟性と厳密性のバランス
小規模な社内ツールや、プロトタイプ開発など、スピードを重視する場合は、記述が簡潔なString型での画面遷移が適しています。しかし、大規模なエンタープライズ開発や、外部に公開する公開APIを開発する場合は、HTTPレスポンスの状態を厳密に定義できるResponseEntity型が推奨されます。
以下のコード例は、処理の結果に応じて画面表示(正常時)とエラーメッセージ(異常時)を動的に制御する、実戦的な設計パターンです。
@Controller
@RequestMapping("/order")
public class OrderController {
@RequestMapping("/process")
public String processOrder(@RequestParam("id") int orderId, Model model) {
// 注文情報の存在チェック(疑似的な業務ロジック)
if (orderId <= 0) {
model.addAttribute("errorMessage", "不正な注文IDが指定されました。");
return "error/badRequest"; // エラー専用の画面を表示
}
// 正常に処理が進んだ場合
model.addAttribute("orderId", orderId);
model.addAttribute("status", "受付完了");
// 完了画面のテンプレート名を返す
return "order/success";
}
}
8. 実装時によくあるエラー(404 Not Foundや解析ミス)の原因と対策
Spring Bootの戻り値処理を実装していると、意図した画面が表示されなかったり、データが正しくブラウザに届かなかったりするトラブルに遭遇することがあります。ここでは、初心者から中級者までが陥りやすい代表的なエラーとその解決策を深掘りします。
404 Not Found:ビューの解決失敗
最も頻繁に発生するのが「404 Not Found」です。コントローラーの戻り値として文字列を返しているのに、Springがその名前に対応するHTMLファイルを見つけられない場合に発生します。
- 原因1:ファイル配置ミス
デフォルトでは「src/main/resources/templates」配下を探します。このディレクトリ直下ではなくサブフォルダに入れている場合、戻り値にもフォルダ名を含める必要があります(例:return "user/list";)。 - 原因2:タイポ(打ち間違い)
Javaコード内の戻り値の文字列と、実際のHTMLファイル名が1文字でも違うと認識されません。大文字・小文字の区別にも注意してください。 - 原因3:拡張子の誤解
ViewResolverの設定で「.html」が自動付与される設定になっている場合、戻り値に「return "index.html";」と書いてしまうと、Springは「index.html.html」というファイルを探しに行ってしまいます。
JSON解析ミス:循環参照とシリアライズエラー
JavaオブジェクトをJSONとして返却する際、ブラウザ側でエラーが出たり、サーバー側で例外が発生したりすることがあります。
例えば、データベースのエンティティ同士が互いを参照し合っている「双方向関連」がある場合、JSON変換ライブラリ(Jackson)が無限ループに陥り、エラー(StackOverflowError)を吐き出すことがあります。
@RestController
@RequestMapping("/api/debug")
public class ApiDebugController {
@RequestMapping("/test")
public Map<String, Object> testJson() {
Map<String, Object> result = new HashMap<>();
result.put("status", "success");
result.put("data", new Object() {
// GetterがないプライベートなフィールドなどはJSON化できずエラーになる場合がある
public String getName() { return "テスト"; }
});
return result;
}
}
文字化けの発生
戻り値として日本語の文字列を直接返却する場合(@ResponseBody Stringなど)、適切な文字エンコーディングが設定されていないと、ブラウザ上で「??」のように文字化けすることがあります。これは、HTTPレスポンスヘッダーの「Content-Type」が「text/plain;charset=ISO-8859-1」などになっていることが原因です。
対策としては、application.propertiesで文字コードを一括指定するか、RequestMappingのプロパティで個別に指定する方法があります。
9. Spring MVCの戻り値処理におけるベストプラクティス
最後に、Spring MVC(Spring BootのWeb機能の核となる部分)において、保守性が高くトラブルの少ない戻り値処理を実現するための黄金律を紹介します。これらの原則を守ることで、チーム開発においても混乱を避けることができます。
1. 戻り値の型を可能な限り統一する
一つのコントローラークラス内では、戻り値の型を統一することをお勧めします。あるメソッドはString、別のメソッドはModelAndViewといった具合にバラバラだと、後からコードを読む開発者が混乱します。現代的な開発では、画面遷移はすべてString型、API応答はすべてResponseEntity型(または特定の共通レスポンスクラス)に統一するのが一般的です。
2. リダイレクトを適切に活用する(PRGパターン)
データの登録や更新を行うPOSTリクエストの処理が終わった後、そのまま「return "success";」としてHTMLを表示させてはいけません。これをやると、ユーザーがブラウザで「再読み込み」ボタンを押した際に、再びPOSTリクエストが送信され、二重登録が発生してしまいます。
必ずreturn "redirect:/path";を使用して、一度ブラウザのURLを切り替えさせる「Post-Redirect-Get (PRG) パターン」を徹底しましょう。
3. 遷移先のパス管理を構造化する
HTMLファイルの数が増えてくると、フォルダ構成が複雑になります。コントローラー側でも、その構造を意識した戻り値を設定してください。例えば、管理画面用のテンプレートは必ず「admin/」フォルダに入れるといったルールを決め、戻り値も「return "admin/dashboard";」のように記述します。
<!-- 推奨されるフォルダ構造の例 -->
src/main/resources/
└── templates/
├── common/ <!-- 共通のヘッダーやフッター -->
├── user/ <!-- 一般ユーザー用画面 -->
│ ├── login.html
│ └── profile.html
└── admin/ <!-- 管理者用画面 -->
├── users.html
└── settings.html
4. 適切なステータスコードの返却
特にAPI開発において重要ですが、異常が発生した際に単に空のデータを返すのではなく、400(リクエストミス)、401(認証未済)、500(サーバーエラー)といった適切なHTTPステータスコードを添えて戻り値を構成してください。これにより、フロントエンド側のエラーハンドリングが格段にスムーズになります。
Spring Bootの戻り値は、単なる関数の終着点ではなく、Webアプリケーションの振る舞いを決定づける極めて重要な要素です。物理パスの解決から、データのシリアライズ、ブラウザへの再送要求まで、戻り値が引き金となって動く「裏側の仕組み」を意識しながら、日々のコーディングに取り組んでみてください。