@ControllerAdvice徹底解説!【初心者向け】

こんにちは!今回は、Spring Frameworkの例外処理やコントローラ横断的なロジックを実装するためのアノテーション、@ControllerAdviceについて詳しく解説します。@ControllerAdviceを使うことで、アプリケーション全体で共通の処理を簡単に管理できるようになります。

@ControllerAdviceとは?

@ControllerAdviceとは、

「全てのコントローラに適用される横断的な処理を定義できるアノテーション」

のことです。

例えば、アプリ全体で共通のエラーハンドリングをしたい場合、@ControllerAdviceを使うと一箇所でエラー処理をまとめて設定できます。
これにより、各コントローラに同じエラーハンドリングを書く必要がなくなり、コードがスッキリします。

主に次のような場面で活用されます。

  • 共通の例外処理
  • 共通のバリデーションエラー処理
  • 横断的なデータバインディングや事前処理

基本的な使い方

まずは、基本的なコード例を見てみましょう。ここでは、エラーハンドリングの例を紹介します。

サーバー側のコード

@ControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(value = { IllegalArgumentException.class })
    public ResponseEntity<String> handleIllegalArgumentException(IllegalArgumentException e) {
        return new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST);
    }
}

このコードでは、アプリケーション内で発生したIllegalArgumentExceptionをキャッチし、適切なエラーメッセージとステータスコード(400 Bad Request)を返しています。@ExceptionHandlerアノテーションを使うことで、特定の例外に対応するハンドラメソッドを簡単に作成できます。

クライアント側のコード(例)

fetch("http://localhost:8080/api/resource", {
    method: 'GET',
})
.then(response => {
    if (!response.ok) {
        throw new Error('Bad Request');
    }
    return response.json();
})
.catch(error => {
    console.error('Error:', error);
});

クライアント側では、エラーレスポンスが返された際にエラーメッセージを表示する例です。

@ControllerAdviceの使い方【応用編】

@ControllerAdviceは、さまざまな場面で応用できます。ここからは、さらに複雑なケースを見ていきましょう。

1. 特定のコントローラだけに適用する

デフォルトでは、@ControllerAdviceは全てのコントローラに適用されますが、特定のコントローラにのみ適用することも可能です。

@ControllerAdvice(assignableTypes = { UserController.class })
public class UserControllerAdvice {

    @ExceptionHandler(value = { NullPointerException.class })
    public ResponseEntity<String> handleNullPointerException(NullPointerException e) {
        return new ResponseEntity<>("User not found", HttpStatus.NOT_FOUND);
    }
}

この例では、UserControllerクラスに対してのみ例外処理が適用されるようにしています。assignableTypes属性で、適用するコントローラを指定できます。

2. 全ての例外を一括で処理

複数の例外を一括で処理したい場合は、共通の例外クラスを利用するか、例外ハンドラメソッドをまとめて定義できます。

@ControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(value = { Exception.class })
    public ResponseEntity<String> handleAllExceptions(Exception e) {
        return new ResponseEntity<>("An error occurred: " + e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
    }
}

この例では、Exceptionクラスを捕捉して、アプリケーション内で発生する全ての例外を一括で処理しています。

3. カスタム例外の処理

自分で定義したカスタム例外を処理することも可能です。たとえば、次のようにカスタム例外クラスを作成し、それに対応するハンドラを定義します。

public class CustomException extends RuntimeException {
    public CustomException(String message) {
        super(message);
    }
}

@ControllerAdvice
public class CustomExceptionHandler {

    @ExceptionHandler(value = { CustomException.class })
    public ResponseEntity<String> handleCustomException(CustomException e) {
        return new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST);
    }
}

カスタム例外が発生した場合、このハンドラが呼び出され、適切なレスポンスが返されます。

4. バリデーションエラーの処理

フォームデータやAPIリクエストのバリデーションエラーも、@ControllerAdviceを使って一括で処理することができます。

@ControllerAdvice
public class ValidationExceptionHandler {

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<Map<String, String>> handleValidationExceptions(MethodArgumentNotValidException ex) {
        Map<String, String> errors = new HashMap<>();
        ex.getBindingResult().getFieldErrors().forEach(error -> {
            errors.put(error.getField(), error.getDefaultMessage());
        });
        return new ResponseEntity<>(errors, HttpStatus.BAD_REQUEST);
    }
}

この例では、リクエストのバリデーションエラーをキャッチして、フィールドごとのエラーメッセージをクライアントに返します。

5. JSONレスポンスを返す

例外が発生した際、エラーメッセージをJSON形式で返すことも可能です。次のコードでは、カスタムのエラーレスポンスをJSONで返しています。

@ControllerAdvice
public class JsonExceptionHandler {

    @ExceptionHandler(value = { IllegalArgumentException.class })
    public ResponseEntity<Map<String, String>> handleIllegalArgumentException(IllegalArgumentException e) {
        Map<String, String> errorResponse = new HashMap<>();
        errorResponse.put("error", e.getMessage());
        return new ResponseEntity<>(errorResponse, HttpStatus.BAD_REQUEST);
    }
}

この方法により、クライアント側でのエラーハンドリングが簡単になります。

6. HTTPヘッダーに基づいたエラーハンドリング

認証情報やその他のヘッダーに基づいてエラーハンドリングを行うこともできます。

@ControllerAdvice
public class HeaderExceptionHandler {

    @ExceptionHandler(value = { IllegalAccessException.class })
    public ResponseEntity<String> handleIllegalAccessException(IllegalAccessException e, @RequestHeader("Authorization") String token) {
        if (token == null || !authService.isValidToken(token)) {
            return new ResponseEntity<>("Invalid token", HttpStatus.UNAUTHORIZED);
        }
        return new ResponseEntity<>("Access denied: " + e.getMessage(), HttpStatus.FORBIDDEN);
    }
}

この例では、認証トークンが無効な場合に401 Unauthorizedを返し、その他のアクセスエラーには403 Forbiddenを返します。

@ControllerAdviceの引数

@ControllerAdviceでは、さまざまな引数を使って処理を細かく制御できます。

1. assignableTypes属性

assignableTypes属性を使うことで、特定のコントローラに対してのみ適用することが可能です。

@ControllerAdvice(assignableTypes = { UserController.class })

2. basePackages属性

basePackages属性を使用して、特定のパッケージのコントローラに対してだけ適用することができます。

@ControllerAdvice(basePackages = "com.example.controllers")

おわりに

@ControllerAdviceを使えば、Springアプリケーション全体で例外処理や共通ロジックを簡単に実装できます。特にエラーハンドリングにおいて、コードの再利用性や可読性が向上し、アプリケーションの品質が高まります。ぜひ、自分のプロジェクトでも活用してみてください!

もっと詳しく知りたい方は、Spring公式ドキュメントを見てみてくださいね。

他の記事が見たい方はこちら!↓↓↓

タイトルとURLをコピーしました