前回はフロント側と API 通信を行う Controller を作成しました。
今回も Controller を作成しますが、例外処理を専門に扱うため役割が少し異なっています。

例外ハンドラとは?
例外処理用の Controller です。
今から、SpringBoot で発生する全ての例外をこの Controller に伝搬させるようにします。
そのため、全ての例外を処理する役割を果たす Controller となります。
ファイル作成
controller フォルダ内に下記ファイルを作成してください。
- ExceptionHandleController.java

ファイル内容
⬇️のコードをコピペしてください。
package com.ozack.todoapp.controller;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import com.ozack.todoapp.exception.DeleteException;
import com.ozack.todoapp.exception.InsertException;
import com.ozack.todoapp.exception.UpdateException;
/* 例外処理を操作するコントローラー */
@RestControllerAdvice
public class ExceptionHandleController {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
/* データの登録に失敗した場合 */
@ExceptionHandler(InsertException.class)
public ResponseEntity<Object> handleInsertException(InsertException e)
{
logger.error("InsertException occurred: {}", e.getMessage());
String message = "該当データの登録に失敗しました。\nお手数ですが再度お試しください。";
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(message);
}
/* データの更新に失敗した場合 */
@ExceptionHandler(UpdateException.class)
public ResponseEntity<Object> handleUpdateException(UpdateException e)
{
logger.error("UpdateException occurred: {}", e.getMessage());
String message = "該当データの更新に失敗しました。\nお手数ですが再度お試しください。";
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(message);
}
/* データの削除に失敗した場合 */
@ExceptionHandler(DeleteException.class)
public ResponseEntity<Object> handleDeleteException(DeleteException e)
{
logger.error("DeleteException occurred: {}", e.getMessage());
String message = "該当データの削除に失敗しました。\nお手数ですが再度お試しください。";
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(message);
}
/* 上記以外の例外が発生した場合 */
@ExceptionHandler(Throwable.class)
public ResponseEntity<Object> handle(Throwable e)
{
logger.error(e.getMessage());
String message = "予期しないエラーが発生しました。\nお手数ですが管理者にお問い合わせください。\n";
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(message + e.getMessage());
/*
* Throwable クラスの例外(全てのエラーや例外を表す基底クラス)を捕捉。
* Throwable は Exception や Error の基底クラスなため、あらゆる種類のエラーや例外がこのメソッドで処理される。
*/
}
/*
* 考慮事項
* ユーザーが操作する必要のある例外 → 操作方法を示すメッセージをユーザーに通知する。
* 管理者が操作する必要のある例外 → 定型文をユーザーに通知する。
*/
}
下記4通りに場合分けして例外をハンドリングしています。
- データ登録時のエラー
- データ更新時のエラー
- データ削除時のエラー
- 上記3つ以外のエラー
ユーザーが操作する必要のある例外には操作を促す指示をレスポンスとして返却します。
管理者でないと何ともできない例外は、管理者に連絡して的な指示を返却してます。
コード内のアノテーションについても少し説明します。
@RestControllerAdvice
例外ハンドラ用の Controller を作成するときに付与します。
このアノテーションを付与すると、あらゆるクラスで発生した例外が、この Controller に伝搬されるようになります。
つまり、SpringBoot で発生する全ての例外は、最終的に ExceptionHandleController に渡されます。
今回の構成だと、
- 各Serviceで例外発生
- 対応する Controller に例外を伝搬
- ExceptionHandleController に例外を伝搬
という流れになります。
@ExceptionHandler
例外を扱うメソッドに付与します。
例えば、InsertException を扱うメソッドなら、下記のように定義します。
/* データの登録に失敗した場合 */
@ExceptionHandler(InsertException.class)
public ResponseEntity<Object> handleInsertException(InsertException e)
{
logger.error("InsertException occurred: {}", e.getMessage());
String message = "該当データの登録に失敗しました。\nお手数ですが再度お試しください。";
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(message);
}
他のメソッドも全く同じ形式で定義可能です。
まとめ
今回は例外ハンドラ専門の Controller を作成しました。
プロジェクトは GitHub にプッシュしています。
これでバックエンド側の開発は完成です!
次回なのですが、目的によって次回記事が変わります。
フロントエンド側の開発も目を通したい場合は⬇️に進んでください。
バックエンド側だけ学びたい方は最終記事の⬇️に進んでください。