こんにちは!今回は、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公式ドキュメントを見てみてくださいね。
他の記事が見たい方はこちら!↓↓↓