きり丸アドベントカレンダー2020の17記事目です。
エラーが発生した時に、共通的にExceptionをキャッチできると、フロントに返却するレスポンスのHttpStatusやBodyを共通化できます。その共通化にはRestControllerAdvise
を使用します。
それぞれ適切なExceptionを出すことにより、トランザクション系のデータが存在しないときはHttpStatus:404、マスタ系のデータが存在しないときはHttpStatus:500を返却したい、というような振り分けをできます。
ぜひ覚えてみましょう。
ゴール
- Exceptionを共通的にキャッチする
環境
手順
Exceptionを拡張する
Exceptionを拡張しておくと、共通的にキャッチするときに見分けがつけられます。そうでなくとも、ライブラリのExceptionをそのまま使用するとライブラリを変更した時にキャッチできなくなってしまいます。
ですので、基本的にライブラリのExceptionから自作Exceptionに翻訳してthrowするのがベターです。
// 自作Exceptionであることを示すException public abstract class BusinessException extends RuntimeException { }
さらに、キャッチしたあとにHttpStatus:404を返却するためのExceptionを作成します。HttpStatusごとにExceptionを作っておき、さらに意図があれば継承したExceptionを作成しましょう。
// HttpStatus:404を返却するためのException public class NotFoundException extends BusinessException { }
Exceptionをキャッチする仕組みを作る
@RestControllerAdvice
を付与したうえで、ResponseEntityExceptionHandler
を継承します。継承しなくても動きますが複数の例外をキャッチしてくれるので、使っておいた方がいいでしょう。
今回は自作したNotFoundExceptionをキャッチしたうえでHttpStatus:404を返却します。@ExceptionHandler
にキャッチ用のExceptionのクラスを指定して、返却値のhandleExceptionInternal
メソッドの第四引数にHttpStatus.NOT_FOUND
を指定します。
Bodyは意識していないので、「not found」の文字列だけを返却するようにしています。
@RestControllerAdvice public class CustomExceptionHandler extends ResponseEntityExceptionHandler { @ExceptionHandler(NotFoundException.class) public ResponseEntity<Object> handleRundtimeException(NotFoundException ex, WebRequest request) { return super.handleExceptionInternal(ex, "not found", null, HttpStatus.NOT_FOUND, request); } }
Exceptionを発生させるコードを書く
16日の記事ではデータが存在しないときは、return ResponseEntity.notFound().build();
とControllerでHttpStatusを指定して返却していました。
それも悪くないですが、今回の仕組みを使用したいのでthrow new NotFoundException();
に書き換えます。
この状態でTodoRestControllerTests.java
のテストを実行して失敗しなければ問題ありません。ちなみにキャッチできないようなExceptionはHttpStatus:500になるので、throw new RuntimeException();
に書き換えたりして確認してみてください。
ファイル名:TodoRestController.java
@GetMapping("/{userId}") public ResponseEntity<List<TodoDto>> get(@PathVariable String userId) { final List<TodoDto> list = todoRepository.findList(userId); if (list.isEmpty()) { throw new NotFoundException(); // データが無いときに自作NotFoundException } else { return ResponseEntity.ok(list); } }
備考
こちらの記事もExceptionHandlerの記事なので、見ていただけるとうれしいです。
【Spring】共通的かつ特別なハンドリングを行いたかった nainaistar.hatenablog.com
ソースコード
アドベントカレンダー17日目。
github.com
終わりに
シンプルにHttpStatusまとめて返却するだけの実装を紹介しましたが、Exceptionの拡張方法によってはログを出力したりレスポンスBodyを加工する等ができます。
RestControllerAdvise
を使用しない場合は各Controllerで頑張ってExceptionをハンドリングする必要があります。必要であればキャッチすべきですが、そこまで頑張ることは多くないと思うので、楽してハンドリングしましょう。
この記事がお役に立ちましたら、各種SNSでのシェアや、今後も情報発信しますのでフォローよろしくお願いします。
類似記事
きり丸アドベントカレンダー2020 adventar.org
きり丸のHerokuページ
https://kirimaru-todoapp.herokuapp.com/
18日目のアドベントカレンダーの記事 https://nainaistar.hatenablog.com/entry/2020/12/18/083000nainaistar.hatenablog.com
【Spring】共通的かつ特別なハンドリングを行いたかった nainaistar.hatenablog.com