3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【Spring Boot】発生した例外ごとのハンドリングを定義する

Last updated at Posted at 2023-03-13

ResponseEntityExceptionHandler について

Spring Boot では ResponseEntityExceptionHandlerクラスで『どの例外が発生したときになにを返すか』のハンドリングを行っています。
このクラスを継承して例外ごとに定義されているメソッドをオーバーライドすることで、処理を好きなように行うことができます。

すべての例外のハンドリングがこのクラスに記述されているわけではないので、クラスの中身を確認してみてください。

ハンドリングの例(HttpRequestMethodNotSupportedException)

処理の例を見ていきます。
POSTメソッドしか定義されていないエンドポイントに対してGETでリクエストされたときなどに発生するHttpRequestMethodNotSupportedExceptionにおいて、任意のレスポンスボディを定義する方法について記述します。

CustomControllerAdvice.java
/**
 * 各エンドポイントで共通の例外ハンドリングを定義する.
 */
@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:8080GETでアクセスすれば処理を確認できます。

SampleController.java
@RestController
public class SampleController {
	
	@PostMapping("/")
	public ResponseEntity<Object> sample() {
        return ResponseEntity.ok(null);
	}
}

自作の例外処理のハンドリング

先程までは既存の例外について見ていきましたが、次は自作した例外が発生したときの処理の書き方についてみていきます。

以下のような、ユーザが見つからなかった際に発生させる例外を定義したとします。

UserNotFoundException.java
public class UserNotFoundException extends RuntimeException {
	private static final long serialVersionUID = 1L;
}

この例外が発生したときの処理を、先程と同じように定義していきます。
こちらにも@RestControllerAdviceが付与されている必要があります。

CustomControllerAdvice.java
@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."]
}

こちらも試したい場合は以下のようなコントローラを作成してGETlocalhost:8080にアクセスすると、動作を確認できます。

SampleController.java
@RestController
public class SampleController {
	
	@GetMapping("/")
	public ResponseEntity<Object> sample() {
		throw new UserNotFoundException();
	}
}

最後に

多くの箇所で発生しうる例外においては、各コントローラで処理するよりも今回のように共通化してしまったほうが楽な場面もあるかと思います。

よりよい記述方法などありましたら、コメントなどしていただけますと幸いです。

3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?