Spring Bootマイクロサービス開発における共通例外処理とCORS設定の実装

Spring MVCにおける主要なアノテーションの役割

REST APIの開発において、以下のアノテーションはリクエストデータのバインディングとレスポンスの生成に不可欠です。

  • @PathVariable: URLパス内のプレースホルダーをメソッドの引数にマッピングします。例えば、/users/{id}というエンドポイントからID値を取得する場合に使用します。
  • @RequestBody: HTTPリクエストのボディに含まれるJSONデータを、Javaオブジェクト(POJO)に変換してバインドします。主にPOSTやPUTリクエストで使用されます。
  • @ResponseBody: メソッドの戻り値を直接HTTPレスポンスボディとして書き込みます。通常はJSON形式にシリアライズされてクライアントに返されます(@RestControllerを使用する場合は省略可能です)。

統一例外処理(グローバル例外ハンドリング)の実装

マイクロサービスアーキテクチャでは、個々のコントローラで例外処理を実装するのではなく、@ControllerAdviceを活用してアプリケーション全体で共通の例外処理を行うことがベストプラクティスです。これにより、エラーレスポンスの形式を統一し、クライアント側でのハンドリングを容易にします。

1. 共通の例外ハンドラクラスの定義

まず、任意の例外をキャッチして標準的なエラーレスポンスを返す基本的なハンドラを作成します。特定の例外クラスが指定されていない場合、最も汎用的なExceptionクラスをキャッチします。

import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.http.ResponseEntity;

@ControllerAdvice
public class GlobalExceptionHandler {

    // 汎用的な例外をキャッチするハンドラ
    @ExceptionHandler(Exception.class)
    @ResponseBody
    public ResponseEntity<ApiResult> handleGeneralException(Exception ex) {
        ex.printStackTrace();
        // スタックトレースはログに出力するのが一般的です
        return ResponseEntity.status(500)
                .body(new ApiResult(500, "システム内部で予期せぬエラーが発生しました。"));
    }
}

2. カスタム例外クラスの作成

業務ロジック特有のエラー(バリデーションエラーや権限エラーなど)を表現するために、RuntimeExceptionを継承した独自の例外クラスを定義します。

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class BusinessException extends RuntimeException {
    private int errorCode;
    private String errorMessage;
}

3. カスタム例外専用のハンドラ実装

定義したBusinessExceptionをキャッチし、その情報に基づいた詳細なエラーレスポンスを返す処理を追加します。複数の@ExceptionHandlerが存在する場合、最も具体的な例外クラス(子クラス)に対応するハンドラが優先して呼び出されます。

@ControllerAdvice
public class GlobalExceptionHandler {

    // ... 既存のハンドラ ...

    @ResponseBody
    @ExceptionHandler(BusinessException.class)
    public ResponseEntity<ApiResult> handleBusinessException(BusinessException ex) {
        ex.printStackTrace();
        // カスタム例外に含まれるコードとメッセージを使用
        return ResponseEntity.status(ex.getErrorCode())
                .body(new ApiResult(ex.getErrorCode(), ex.getErrorMessage()));
    }
}

4. 例外のスロー

コントローラやサービス層のロジック内でエラー条件を検知した際は、以下のようにして手動でカスタム例外をスローします。

throw new BusinessException(400, "リクエストパラメータが不正です。");

クロスオリジンリソースシェアリング(CORS)への対処

フロントエンド(例: ポート3000で動作するReactアプリ)とバックエンド(例: ポート8080で動作するSpring Boot)が異なるオリジン(プロトコル、ドメイン、ポートのいずれかが異なる状態)で通信しようとする場合、ブラウザのセキュリティポリシーによりCORSエラーが発生します。

解決策:

  1. アノテーションによる設定: 特定のコントローラクラスまたはメソッドに@CrossOriginアノテーションを付与することで、そのエンドポイントへのクロスオリジンアクセスを許可します。手軽ですが、大規模なプロジェクトでは管理が分散する可能性があります。
  2. APIゲートウェイによる設定: マイクロサービスアーキテクチャでは、APIゲートウェイ(例: Spring Cloud Gateway)層でCORS設定を一元管理する方法が推奨されます。これにより、個々のマイクロサービスの実装詳細を意識せずに、セキュリティポリシーを統一できます。

タグ: SpringBoot Java REST CORS ExceptionHandling

6月28日 02:35 投稿