ResponseEntityExceptionHandler について
Spring Boot では ResponseEntityExceptionHandler
クラスで『どの例外が発生したときになにを返すか』のハンドリングを行っています。
このクラスを継承して例外ごとに定義されているメソッドをオーバーライドすることで、処理を好きなように行うことができます。
すべての例外のハンドリングがこのクラスに記述されているわけではないので、クラスの中身を確認してみてください。
ハンドリングの例(HttpRequestMethodNotSupportedException)
処理の例を見ていきます。
POST
メソッドしか定義されていないエンドポイントに対してGET
でリクエストされたときなどに発生するHttpRequestMethodNotSupportedException
において、任意のレスポンスボディを定義する方法について記述します。
/**
* 各エンドポイントで共通の例外ハンドリングを定義する.
*/
@RestControllerAdvice
public class CustomControllerAdvice extends ResponseEntityExceptionHandler {
/**
* HttpRequestMethodNotSupportedException(405)のハンドリング.
*/
@Override
protected ResponseEntity<Object> handleHttpRequestMethodNotSupported(
HttpRequestMethodNotSupportedException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
Map<String, Object> res = Map.of("errorMessages", List.of("Method not supported."), "status", "406");
return handleExceptionInternal(ex, res, headers, status, request);
}
}
まず、ResponseEntityExceptionHandler
を継承した任意のクラスを作成します。(今回クラス名はCustomControllerAdvice
としています。)
次にクラスに@RestControllerAdvice
のアノテーションを付ける必要があります。
そして該当するメソッドをオーバーライドしていきます。
メソッドはhandle〇〇
となっており、〇〇
の部分は例外の名前からException
を外したものです。
return
しているhandleExceptionInternal
メソッドの引数には以下を入れる形になっています。
- 第1引数:例外
- 第2引数:レスポンスボディ
- 第3引数:レスポンスヘッダ
- 第4引数:HTTPステータス
- 第5引数:
WebRequest
今回は、HttpRequestMethodNotSupportedException
が発生したとき、res
という名前のMapを定義して、それをレスポンスボディに入れるということを行っています。
実際のレスポンスボディの中身は以下のようになります。
{
"status":"406",
"errorMessages":["Method not supported."]
}
試したい場合は以下のような簡単なコントローラを作成し、アプリケーションを起動後localhost:8080
にGET
でアクセスすれば処理を確認できます。
@RestController
public class SampleController {
@PostMapping("/")
public ResponseEntity<Object> sample() {
return ResponseEntity.ok(null);
}
}
自作の例外処理のハンドリング
先程までは既存の例外について見ていきましたが、次は自作した例外が発生したときの処理の書き方についてみていきます。
以下のような、ユーザが見つからなかった際に発生させる例外を定義したとします。
public class UserNotFoundException extends RuntimeException {
private static final long serialVersionUID = 1L;
}
この例外が発生したときの処理を、先程と同じように定義していきます。
こちらにも@RestControllerAdvice
が付与されている必要があります。
@RestControllerAdvice
public class CustomControllerAdvice extends ResponseEntityExceptionHandler {
/**
* UserNotFoundException(自作クラス)のハンドリング.
* 404を返す
*/
@ExceptionHandler
protected ResponseEntity<Object> handleUserNotFound(UserNotFoundException ex, WebRequest request) {
Map<String, Object> res = Map.of("errorMessages", List.of("User not found."), "status", "400");
return handleExceptionInternal(ex, res, new HttpHeaders(), HttpStatus.NOT_FOUND, request);
}
}
まず、メソッドには@ExceptionHandler
を付与します。
メソッド名は任意で問題ありませんが、handle〇〇
のように既存の例外処理のメソッド名と揃えるとわかりやすいと思います。
第1引数には自作した例外クラス、第2引数にはWebRequest
を入れるようにします。
今回も先程と同様、res
という変数にレスポンスボディの値を定義しています。
定義したレスポンスボディは以下のようになります。
{
"status":"400",
"errorMessages":["User not found."]
}
こちらも試したい場合は以下のようなコントローラを作成してGET
でlocalhost:8080
にアクセスすると、動作を確認できます。
@RestController
public class SampleController {
@GetMapping("/")
public ResponseEntity<Object> sample() {
throw new UserNotFoundException();
}
}
最後に
多くの箇所で発生しうる例外においては、各コントローラで処理するよりも今回のように共通化してしまったほうが楽な場面もあるかと思います。
よりよい記述方法などありましたら、コメントなどしていただけますと幸いです。