Spring Bootの404 Not Foundについて、ControllerのURLとRequestMappingの確認順を解説する記事のアイキャッチ。

Spring

Spring Bootで404 Not Foundになる原因|ControllerのURLが合わないときの確認順

AIによる要約

Spring Bootの404 Not Foundは、リクエストされたURLに対応するControllerや静的リソースが見つからないときに発生します。まずブラウザやAPIクライアントが実際に送ったURLとHTTPメソッドを確認し、次にクラスとメソッドのRequestMappingを連結した完成URL、context-path、ControllerのBean登録を確認します。Controllerへ到達した後の例外は通常500であり、GETとPOSTだけが違う場合は405です。エラー画面の見た目ではなく、ステータスコード、アクセスログ、ブレークポイント、MockMvcテストを使って切り分けます。

新人SE
新人SE
Controllerは書いたのに、ブラウザから開くと404になります。どこから確認すればよいですか?
ポンコツSE
ポンコツSE
最初に見るのはJavaコードではなく、実際に送信されたURLとHTTPメソッドです。その後にMappingを連結して比べます。

Spring Bootの画面やAPIを追加した直後に、404 Not FoundやWhitelabel Error Pageが表示されることがあります。新人の現場では「Controllerが動いていない」と考えてServiceやRepositoryまで読み始めがちですが、404の段階ではControllerのメソッドへ到達していない可能性が高いです。

確認対象を広げすぎると、単純なURLの1文字違いに長時間使ってしまいます。この記事では、Spring MVCのリクエストがどのControllerへ割り当てられるかを理解し、現場で再現しやすい順番に原因を切り分けます。

この記事のポイント

  • 404はURLに対応するハンドラやリソースが見つからない状態
  • クラスとメソッドのMappingを連結して完成URLを確認する
  • GETとPOSTの違いは404ではなく405になることがある
  • Controllerがコンポーネントスキャン対象か確認する
  • context-pathやリバースプロキシ配下のパスを見落とさない
  • MockMvcで正しいURLと誤ったURLの両方をテストする

Spring Bootの404 Not Foundとは

HTTP 404は、クライアントが指定した場所に対応するものが見つからないことを表します。Spring MVCでは、URL、HTTPメソッド、パラメータ、ヘッダー、Content-Typeなどを使って、リクエストをControllerのメソッドへ割り当てます。

たとえば、次のControllerがあるとします。

@RestController
@RequestMapping("/api/users")
public class UserController {

    @GetMapping("/{id}")
    public UserResponse findById(@PathVariable Long id) {
        return userService.findById(id);
    }
}

完成するURLは/api/users/{id}です。IDが10なら、GETで/api/users/10へアクセスします。/users/10/api/user/10では一致しません。

404を見たら、まず「Controllerの中で何が失敗したか」ではなく、「そのControllerが選ばれたか」を確認します。

最初に実際のURLとHTTPメソッドを確認する

設計書に書かれたURLや、自分が呼んだつもりのURLではなく、実際に送信されたリクエストを確認します。ブラウザなら開発者ツールのNetwork、PostmanなどのAPIクライアントなら履歴、フロントエンドなら通信ログを見ます。

  • ホスト名とポートは正しいか
  • URLの単数形・複数形は合っているか
  • 大文字小文字やハイフンを間違えていないか
  • パス変数を入れ忘れていないか
  • GET、POST、PUT、DELETEは合っているか
  • 末尾へ余計なパスを付けていないか

画面のリンクとControllerを別の担当者が実装している場合、両方がそれぞれ正しそうに見えても、URLの契約がずれていることがあります。レビューでは片側だけでなく、呼び出し元と受け取り側を並べて確認します。

RequestMappingとGetMappingを連結する

SpringのMappingは、クラス単位とメソッド単位に分かれていることがあります。メソッドの@GetMappingだけを見てURLを判断しないでください。

@Controller
@RequestMapping("/admin/orders")
public class OrderController {

    @GetMapping("/detail/{orderId}")
    public String detail(
            @PathVariable Long orderId,
            Model model) {
        model.addAttribute("order", orderService.find(orderId));
        return "order/detail";
    }
}

この場合のURLは/admin/orders/detail/{orderId}です。戻り値のorder/detailはThymeleafなどのテンプレート名であり、アクセスURLではありません。URLとView名を混同すると、存在しないパスへアクセスして404になります。

PathVariableの数と位置も確認する

@GetMapping("/{companyId}/users/{userId}")なら、会社IDと利用者IDの両方が必要です。片方をクエリパラメータとして送っても、パスの形が一致しません。

正しい例:
GET /api/companies/10/users/25

誤った例:
GET /api/companies/users/25?companyId=10

404・405・400・500を分けて考える

ステータス代表的な状態最初の確認
404URLに対応するハンドラがないパス、Mapping、Bean登録
405URLはあるがHTTPメソッドが違うGET・POST・PUT・DELETE
400型変換、必須値、JSONなどが不正入力値、Content-Type
500処理中に未処理例外が発生ログ、Caused by、Service・DB

ステータスコードを分けるだけで、読むべき範囲が変わります。404なのにServiceのSQLを調べたり、500なのにURLだけを変更したりすると、調査が遠回りになります。

ControllerがBean登録されているか確認する

URLが正しくても、ControllerクラスがSpringの管理対象でなければMappingは登録されません。代表例は、@Controller@RestControllerの付け忘れです。

// NG: 普通のJavaクラスなのでSpring MVCへ登録されない
public class ReportController {

    @GetMapping("/reports")
    public String list() {
        return "report/list";
    }
}
// OK: SpringのControllerとして登録される
@Controller
public class ReportController {

    @GetMapping("/reports")
    public String list() {
        return "report/list";
    }
}

もう一つ多いのが、起動クラスのパッケージ配下からControllerが外れているケースです。通常の@SpringBootApplicationは、そのクラスが置かれたパッケージを基準にコンポーネントスキャンします。

com.example.app
├── Application.java        // 起動クラス
├── controller              // 通常はスキャン対象
└── service

com.example.external
└── ReportController.java   // 設定次第では対象外

場当たり的にscanBasePackagesを広げる前に、プロジェクトのパッケージ構成と既存Controllerの置き場所へ合わせるのが安全です。

context-pathとプロキシ配下のパスを確認する

ローカルでは動くのに結合環境だけ404になる場合、アプリケーションの前にロードバランサー、Webサーバー、API Gatewayなどが置かれていることがあります。また、Spring Boot側でcontext-pathを設定している場合もあります。

server.servlet.context-path=/sales

この設定があると、ControllerのMappingが/api/ordersでも、アプリケーションへ直接アクセスするときのURLは/sales/api/ordersになります。環境ごとの設定ファイル、起動引数、環境変数も確認します。

注意

本番環境の404だけを見てControllerを変更すると、プロキシのパス変換と二重に修正してしまうことがあります。ブラウザから見えるURL、プロキシが転送するURL、Springが受け取るURLを分けて記録してください。

静的ファイル・画面テンプレート・APIを混同しない

同じ404でも、対象が何かで確認場所が違います。

  • API: ControllerのMappingを確認する
  • CSS・JavaScript・画像: src/main/resources/staticなどの配置を確認する
  • Thymeleaf画面: Controllerの戻り値とtemplates配下のファイル名を確認する
  • DB上のデータ: URLは合っていても、未存在を意図的に404へ変換している場合がある

「404だからControllerがない」と決めつけず、レスポンス本文、ログ、対象URLの役割を見ます。データ未存在をResponseStatusException(HttpStatus.NOT_FOUND)へ変換している設計なら、Controllerへ到達した後でも404になります。

MockMvcでMappingを固定する

URLの契約はテストで固定できます。正常なURLが200になることだけでなく、HTTPメソッドや必須パスを間違えた場合のステータスも確認すると、改修時のMapping変更に気づきやすくなります。

@WebMvcTest(UserController.class)
class UserControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private UserService userService;

    @Test
    void 利用者詳細を取得できる() throws Exception {
        given(userService.findById(10L))
                .willReturn(new UserResponse(10L, "Tanaka"));

        mockMvc.perform(get("/api/users/{id}", 10L))
                .andExpect(status().isOk());
    }
}

文字列を手入力する箇所が複数ある場合は、API仕様やフロント側の定数との整合も確認します。ただし、URL定数を共通化しすぎて変更箇所が見えなくなる場合もあるため、既存プロジェクトの方針へ合わせます。

現場で使える404の確認順

  1. NetworkやAPIクライアントで実際のURLとHTTPメソッドを記録する
  2. ステータスが本当に404か確認する
  3. クラスとメソッドのMappingを連結する
  4. context-pathとプロキシの転送パスを加える
  5. Controllerへブレークポイントを置いて到達有無を見る
  6. ControllerがBean登録・スキャンされているか確認する
  7. 静的ファイル、テンプレート、データ未存在を切り分ける
  8. MockMvcで再現する

コードレビューで伝えるコメント例

「URLが間違っています」だけでは、呼び出し側とControllerのどちらを直すべきか分かりません。契約と根拠を含めて伝えます。

画面は/admin/order/detail/10を呼んでいますが、Controllerの完成URLは/admin/orders/detail/10です。API仕様書では複数形のordersになっているため、画面側のリンクを合わせる認識でよいでしょうか。あわせてMockMvcへ正常URLのテストを追加したいです。

この書き方なら、現象、差分、正とする根拠、再発防止が一度に伝わります。

提出前のセルフチェック

レビュー前に確認すること

  • 実際のURLとHTTPメソッドを確認したか
  • クラスとメソッドのMappingを連結したか
  • PathVariableの数と順番が合っているか
  • context-pathを含めたか
  • Controllerへ到達しているか
  • ControllerがBean登録されているか
  • 静的ファイルとView名をURLと混同していないか
  • 404と405・400・500を区別したか
  • 正常URLをMockMvcでテストしたか
  • 環境固有のプロキシ設定を確認したか

Spring MVCを体系的に確認する参考書

404の原因調査では、Controllerのアノテーションだけでなく、リクエストがSpring MVCへ届いてからメソッドへ割り当てられる流れを理解していると判断が速くなります。

書籍「作って学ぶ Spring Boot入門」の表紙
商品画像:Amazon.co.jp
PR Java新人・若手向け

作って学ぶ Spring Boot入門

多田 真敏 (著)

Spring Bootを、動くWebアプリを作りながら覚える。

Controller、画面、DB連携などを一つのアプリとして実装できます。Java基礎からSpringの現場へ進む橋渡しに向く一冊です。

  • Spring Bootを手を動かして学びたい
  • Webアプリ開発の流れをつかみたい

当サイトはAmazonアソシエイト・プログラムの参加者です。価格・在庫・配送条件はAmazonでご確認ください。

この記事とあわせて読みたい

まとめ

Spring Bootの404 Not Foundでは、実際に送信されたURLとHTTPメソッドを最初に確認します。クラスとメソッドのMapping、PathVariable、context-pathを組み合わせた完成URLと比較してください。

URLが合っている場合は、ControllerのBean登録、コンポーネントスキャン、プロキシ設定、静的ファイルやテンプレートの配置へ進みます。404、405、400、500を分け、ブレークポイントとMockMvcでControllerへの到達を確認すれば、調査範囲を必要以上に広げずに済みます。

Spring MVCのMapping仕様はSpring Framework公式ドキュメントでも確認できます。

-Spring
-, , ,