カテゴリ: Play Framework 更新日: 2026/01/18

Play Frameworkの例外処理ベストプラクティス!コントローラでのエラー対策

コントローラで例外を処理するベストプラクティス
コントローラで例外を処理するベストプラクティス

先生と生徒の会話形式で理解しよう

生徒

「Play Frameworkでアプリを作っているのですが、プログラムでエラーが起きたときに真っ白な画面や英語の難しいエラーメッセージが出てしまいます。もっと格好よく処理する方法はありますか?」

先生

「それは『例外処理』が適切に設定されていないからですね。例外処理をマスターすれば、予期せぬトラブルが起きてもユーザーに優しい画面を表示させたり、裏側でこっそりログを保存したりできるようになりますよ。」

生徒

「例外処理……Javaの授業で習った try-catch みたいなものですか?」

先生

「基本はそうですが、Play Frameworkにはもっとスマートな『ベストプラクティス』があるんです。さっそく見ていきましょう!」

1. 例外処理とは?

1. 例外処理とは?
1. 例外処理とは?

プログラミングにおける例外処理(れいがいしょり)とは、プログラムの実行中に発生する「予期せぬエラー」を捕まえて、適切に処理することです。例えば、存在しないファイルを開こうとしたり、インターネット接続が突然切れたり、計算でゼロによる割り算をしてしまったときに発生します。

Webアプリケーション開発において、何も対策をしていないと、サーバーの内部情報がユーザーに見えてしまうことがあります。これはセキュリティ上非常に危険です。適切なコントローラの設計によって、エラーをキャッチし、安全なレスポンスを返すことが、プロのWebエンジニアへの第一歩です。

2. コントローラ内での try-catch による基本処理

2. コントローラ内での try-catch による基本処理
2. コントローラ内での try-catch による基本処理

もっとも基本的な方法は、Javaの標準的な構文である try-catch ブロックを使うことです。これは「これを試してみて(try)、もしエラーが出たら捕まえてね(catch)」という命令です。Play Frameworkのコントローラメソッド内でこれを使うことで、個別にエラーページへ誘導できます。

パソコンを触ったことがない方でも、「もし転んだら(例外)、保健室に行く(例外処理)」というルールを決めておくことだと考えれば、イメージしやすいはずです。


package controllers;

import play.mvc.*;
import java.util.Optional;

public class BasicErrorController extends Controller {

    public Result showProfile(int userId) {
        try {
            // 本来はここでデータベースからユーザーを探す処理などを書く
            if (userId <= 0) {
                throw new IllegalArgumentException("不正なIDです");
            }
            return ok("ユーザーID: " + userId + " のプロフィールを表示します。");
        } catch (IllegalArgumentException e) {
            // エラーを捕まえて、400 Bad Requestレスポンスを返す
            return badRequest("リクエストが正しくありません: " + e.getMessage());
        } catch (Exception e) {
            // その他の予期せぬエラーは 500 Internal Server Error
            return internalServerError("サーバー内部で問題が発生しました。");
        }
    }
}

3. カスタムエラーページの作成と遷移

3. カスタムエラーページの作成と遷移
3. カスタムエラーページの作成と遷移

先ほどの例では文字列を返していましたが、実際のサービスでは可愛らしいイラスト付きの「お探しのページは見つかりませんでした」といった専用のHTML画面を表示したいですよね。Play Frameworkのビュー(Twirlテンプレート)機能と組み合わせることで、エラー時もブランドイメージを崩さない対応が可能です。

例えば、views/errors/notFound.scala.html というファイルを作っておき、例外をキャッチした際にそのビューを render して返却します。これにより、ユーザーは「故障かな?」と不安にならずに済みます。


@(message: String)

<!DOCTYPE html>
<html>
    <head>
        <title>エラーが発生しました</title>
        <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css">
    </head>
    <body class="container mt-5">
        <div class="alert alert-danger">
            <h1>申し訳ありません!</h1>
            <p>@message</p>
            <a href="/" class="btn btn-primary">トップへ戻る</a>
        </div>
    </body>
</html>

4. HttpErrorHandlerによる一括管理

4. HttpErrorHandlerによる一括管理
4. HttpErrorHandlerによる一括管理

すべてのコントローラに try-catch を書くのは、プログラミングの世界では「重複(DRY原則に反する)」と呼ばれ、あまり良くない書き方とされています。そこで、Play Frameworkが提供するHttpErrorHandlerという仕組みを使いましょう。これは「アプリ全体の門番」のようなもので、どこでエラーが起きても一箇所でまとめて処理できます。

開発環境構築の際に、このエラーハンドラを定義しておくだけで、個別のコントローラがシンプルになり、メンテナンス性が劇的に向上します。これが中級者以上の開発環境におけるベストプラクティスです。


package handlers;

import play.http.HttpErrorHandler;
import play.mvc.*;
import play.mvc.Http.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import javax.inject.Singleton;

@Singleton
public class ErrorHandler implements HttpErrorHandler {
    @Override
    public CompletionStage<Result> onClientError(RequestHeader request, int statusCode, String message) {
        // クライアント側のミス(404など)をここで一括処理
        return CompletableFuture.completedFuture(
                Results.status(statusCode, "クライアントエラーが発生しました。")
        );
    }

    @Override
    public CompletionStage<Result> onServerError(RequestHeader request, Throwable exception) {
        // サーバー側の深刻なエラー(500)をここで一括処理
        return CompletableFuture.completedFuture(
                Results.internalServerError("サーバーで予期せぬエラーが起きました: " + exception.getMessage())
        );
    }
}

5. 適切なHTTPステータスコードの使い分け

5. 適切なHTTPステータスコードの使い分け
5. 適切なHTTPステータスコードの使い分け

例外を処理するとき、どのような「ステータスコード」を返すかが非常に重要です。ステータスコードとは、ブラウザとサーバーが会話するための「番号」です。代表的なものを覚えておきましょう。

  • 400 Bad Request: 送られてきたデータが間違っているとき。
  • 401 Unauthorized: ログインが必要なとき。
  • 403 Forbidden: 権限がないとき。
  • 404 Not Found: ページが存在しないとき。
  • 500 Internal Server Error: プログラムがバグで止まったとき。

これらの番号を適切に返すことで、開発ツール(デベロッパーツール)を使ったデバッグがしやすくなり、また検索エンジン(SEO)に対しても「このページは今一時的に壊れています」といった正しい情報を伝えることができます。

6. ログ出力と監視の重要性

6. ログ出力と監視の重要性
6. ログ出力と監視の重要性

エラー画面を表示するだけで満足してはいけません。開発者は「いつ、どこで、なぜ」エラーが起きたかを知る必要があります。そこで、ロギング(Log)という機能を使います。Play Frameworkでは標準の Logger を使って、エラー内容をファイルに書き出すことができます。

「ユーザーには優しいメッセージを、自分たちには詳細なデバッグ情報を」というのが、リクエストとレスポンス設計の極意です。ログがあれば、ユーザーから報告を受ける前に問題を修正することも可能になります。


import play.Logger;

public Result deleteItem(Long id) {
    try {
        // 削除処理...
        return ok("削除完了");
    } catch (Exception e) {
        // 開発者向けにログを出力(エラーレベル)
        Logger.error("アイテムの削除に失敗しました。ID: " + id, e);
        return internalServerError("削除中にエラーが起きました。");
    }
}

7. 非同期処理における例外のハンドリング

7. 非同期処理における例外のハンドリング
7. 非同期処理における例外のハンドリング

少し難しい内容ですが、Play Frameworkが得意とする非同期処理(CompletionStage)の際のエラー処理についても触れておきます。非同期処理の中では通常の try-catch が使えないため、exceptionally というメソッドを使います。

これは、重たい計算や外部通信が終わるのを待っている間にエラーが起きたとき、後からそのエラーを処理する予約をしておく仕組みです。これを知っていると、高機能なモダンアプリを開発する際に非常に役立ちます。プロジェクトのディレクトリ構成を意識しつつ、サービス層でのエラーも適切に伝搬させましょう。

8. 入力値バリデーションによる例外の未然防止

8. 入力値バリデーションによる例外の未然防止
8. 入力値バリデーションによる例外の未然防止

最高の例外処理とは、「例外を起こさせないこと」です。ユーザーがフォームに入力したデータが正しいかどうかを事前にチェックすることをバリデーションと呼びます。例えば、年齢の欄に「あいうえお」と入力されたら、プログラムが止まる前に「数字を入れてください」と注意する仕組みです。

Play Frameworkにはフォーム処理とバリデーションの強力な機能が備わっています。これらをルーティングの設定と組み合わせて活用することで、プログラムの根幹まで汚いデータが入り込むのを防ぎ、結果として例外の発生回数を劇的に減らすことができます。

9. セッション管理とエラーの関係

9. セッション管理とエラーの関係
9. セッション管理とエラーの関係

時々、ログインの有効期限が切れていることでエラーが発生することがあります。このような場合、単にエラーを表示するのではなく、ログイン画面へリダイレクトさせるのが親切な設計です。セッション管理とクッキーの仕組みを理解しておけば、エラーの種類に応じて「もう一度ログインしてください」といったナビゲーションが作れるようになります。

Webアプリ開発は、こうした「もしも」の積み重ねです。一つひとつの例外に対して、ユーザーが迷子にならないような道筋を作ってあげることが、使いやすいシステムを作るための秘訣です。まずは身近な try-catch から始めて、徐々に HttpErrorHandler を使った洗練された設計に挑戦してみてくださいね!

記事のテキスト部分(平仮名・カタカナ・漢字のみ)の文字数は約2,870文字です。

カテゴリの一覧へ
新着記事
New1
Jakarta EE
Jakarta EEとSpringの比較|どちらを選ぶべきか?初心者向けに徹底解説!
New2
Play Framework
Play Frameworkの例外処理ベストプラクティス!コントローラでのエラー対策
New3
Jakarta EE
Jakarta EEのGradle+Docker連携ガイド!初心者でもできるビルド環境の作り方
New4
Play Framework
Play Frameworkで非同期コントローラを実装!CompletionStageとFutureの活用術
人気記事
No.1
Java&Spring記事人気No1
Jakarta EE
Jakarta EEとJava EEアプリの互換性を完全解説!移行で困らないための基礎知識
No.2
Java&Spring記事人気No2
Play Framework
Play FrameworkでJSONを扱う方法!リクエスト受信とレスポンス返却
No.3
Java&Spring記事人気No3
Jakarta EE
Jakarta EEとSpringの比較|どちらを選ぶべきか?初心者向けに徹底解説!
No.4
Java&Spring記事人気No4
Jakarta EE
Jakarta サーブレットのHttpServletRequestを徹底解説!初心者でもわかる基本操作と使い方
No.5
Java&Spring記事人気No5
Jakarta EE
Jakarta Server Pages(JSP)のセキュリティ上の注意点を完全解説!初心者でも理解できるXSS対策の基本
No.6
Java&Spring記事人気No6
Jakarta EE
Jakarta サーブレットのdoGetとdoPostの違いと使い分けを徹底解説!初心者でもわかるHTTPリクエスト処理
No.7
Java&Spring記事人気No7
Jakarta EE
Jakarta EEプロジェクト用のpom.xml基本設定を完全解説!初心者向けMaven構成ガイド
No.8
Java&Spring記事人気No8
Jakarta EE
Jakarta Server Pages(JSP)とJSTLの使い分けを徹底解説!初心者でも迷わない基本と考え方