0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

SpringBootのexception handlerを使ってみましょう

Last updated at Posted at 2024-11-30

はじめに

筆者はSIerでエンジニアをしています。

今日はexception handlerについてまとめてみます。

exception handlerとは

アプリケーションから発生した例外に対する処理を一箇所で管理できます。
また、それぞれのexceptionに対して、処理を実装できます。

実際に見てみましょう。

まずはexceptionが発生するAPIを用意する

なんらかexceptionが発生するAPIがあるとします。

この実装だと、エラーがSpringBootデフォルトのハンドリングが行われます。
そして、Whitelabel Error Pageのようなものがレスポンスとして返されます。

あまり嬉しくないですね。

@RestController
@RequestMapping("/api")
public class SampleApi2 {

    @GetMapping("/hello2")
    public ResponseEntity<String> getHello() {
        // エラーが発生する処理
        doSomething();
        return ResponseEntity.ok("Hello, World!");
    }

    private void doSomething() {
        throw new RuntimeException();
    }
}

てことでexception発生時の処理を実装する

try-catchを使用して、エラー時の処理を実装します。
これでしっかりアプリケーションとして、例外の制御もできるようになりました。

@RestController
@RequestMapping("/api")
public class SampleApi2 {

    @GetMapping("/hello2")
    public ResponseEntity<String> getHello() {

        try {
            // エラーが発生する処理
            doSomething();
            return ResponseEntity.ok("Hello, World!");
        } catch (Exception e) {
            return ResponseEntity.status(500).body("Internal Server Error");
        }
    }

    private void doSomething() {
        throw new RuntimeException();
    }
}

ただし、このAPIがたくさんあったら、いちいち面倒ですね。
ここでexception handlerです。

exception handlerを実装する

@RestControllerAdvice@ExceptionHandlerを使用して、exception handlerを実装しましょう。

@RestControllerAdvice自体は、ExceptionHandler以外にも使用します。今回はexception handlerの実装を目的として使用します。

@RestControllerAdvice
public class SampleExceptionHandler {

    @ExceptionHandler(Exception.class)
    public ResponseEntity<String> handleException(Exception ex) {
        // エラーが発生した場合の処理
        return ResponseEntity.status(500).body("Internal Server Error");
    }
}

これによって、controllerでの個別実装が不要になります。

@GetMapping("/hello3")
public ResponseEntity<String> getHello3() {

    // エラーが発生する処理
    doSomething();
    return ResponseEntity.ok("Hello, World!");
}

ということで、APIが増えても一箇所にまとめられて良いですね。

それぞれのexceptionをそれぞれ処理する。

さて、それでも発生する例外は一種類であありません。各exceptionにそれぞれ処理を実装したい場合は、アノテーションに渡すクラスをそれぞれ記載します。

    @ExceptionHandler(Exception.class)
    public ResponseEntity<String> handleException(Exception ex) {
        // エラーが発生した場合の処理
        return ResponseEntity.status(500).body("Internal Server Error");
    }

    @ExceptionHandler(RuntimeException.class)
    public ResponseEntity<String> handleRuntimeException(RuntimeException ex) {
        // エラーが発生した場合の処理
        return ResponseEntity.status(500).body("Runtime Error");
    }

    @ExceptionHandler(IllegalArgumentException.class)
    public ResponseEntity<String> handleIllegalArgumentException(IllegalArgumentException ex) {
        // エラーが発生した場合の処理
        return ResponseEntity.status(400).body("Bad Request");
    }

で、(絶対ないですが)こんなAPIがあったとします。

    // runtime exceptionを発生させるAPI
    @GetMapping("/hello4")
    public ResponseEntity<String> getHello4() {
        throw new RuntimeException();
    }

    // illegal argument exceptionを発生させるAPI
    @GetMapping("/hello5")
    public ResponseEntity<String> getHello5() {
        throw new IllegalArgumentException();
    }

この結果、各APIで発生した例外が、各handlerで捕捉されます。

API エンドポイント 発生する例外 処理するハンドラー HTTPステータス レスポンスボディ
/api/hello4 RuntimeException handleRuntimeException 500 "Runtime Error"
/api/hello5 IllegalArgumentException handleIllegalArgumentException 400 "Bad Request"
その他 Exception handleException 500 "Internal Server Error"

独自例外も含めて個別に処理を実装できていい感じですね。

それでは全部のexceptionのhandlerを実装しないといけないのか?

と思いますが、そうではありません、java.lang.Exceptionに対するhandlerを実装すれば、他のhandler@ExceptionHandlerで捕捉されないものが、全て捕捉されます。

これはJavaの例外クラスの継承構造によるものです。すべての例外はjava.lang.Exceptionクラスを継承しています。

Exception(↑ここが頂点)
    ├── RuntimeException
    │       └── IllegalArgumentException
    ├── IOException
    └── その他の例外クラス

このように、全ての例外はExceptionクラスの子クラスになっているため、@ExceptionHandler(Exception.class)と記述することで、他のhandlerで捕捉されなかった全ての例外をキャッチすることができます。

なので、よく発生する例外は個別にhandlerを実装し、想定外の例外はExceptionのhandlerで捕捉する、という実装で十分なのです。

継承とか、全ての例外はExceptionの子クラスとか、その辺はJavaの言語として理解を進めましょう。

まとめ

今回はexception handlerについてまとめました。始めた時は個別にエラーハンドリングを実装しがちだったのですが、この仕組みはとても便利ですね。

今回は簡単な例でしたが、エラー用のログを出力したり、いろいろできそうです。
では今回は以上です。

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?