web-dev-qa-db-ja.com

Spring BootのErrorControllerとSpringのResponseEntityExceptionHandlerを正しく使用する

質問

Spring Bootでコントローラーを作成して、カスタムの方法でallエラー/例外を処理する場合、-カスタムの例外を含むのどちらの手法を推奨しますか?

  1. コントローラはSpring BootのErrorControllerを実装する必要がありますか?

  2. コントローラはSpringのResponseEntityExceptionHandlerを拡張する必要がありますか?

  3. 両方:両方の機能を含む両方のクラスを実装および拡張する単一のコントローラー?

  4. 両方:2つの別個のコントローラー。1つはErrorControllerを実装し、もう1つはResponseEntityExceptionHandlerを拡張していますか?

目標

この投稿の理由は、次の属性の all を使用してSpring Bootで例外処理の方法を見つけるためです。

  • リクエストの処理中にコントローラー/フィルター/インターセプターで発生するすべてのThrowablesをキャッチする必要があります。
  • Throwableをキャッチする場合、しないスタックトレースまたは他の実装の詳細をクライアントに公開したいever(明示的にコーディングされている場合を除く)そのように)。
  • 発生したすべてのThrowablesをクラスごとに個別に処理できるはずです。その他の指定されていないタイプの場合、デフォルトの応答を指定できます。 (確かに、これは /@ExceptionHandlerで可能なことです。しかしErrorController?)
  • この目標を達成するための醜い回避策やUBはなく、コードはできるだけクリーンで明示的なものにする必要があります。

もっと詳しく

両方のコントローラー(上記の1と2を参照)にResponseEntityオブジェクトを返すメソッドが含まれている可能性があることに気づきました。これにより、発生した例外を処理し、クライアントに応答を返します。だから彼らは理論的には同じ結果を生み出すことができるのでしょうか?

テクニック1と2の使用に関するチュートリアルはいくつかありますが、両方のオプションを検討したり、それらを比較したり、一緒に使用したりする記事は見当たらないため、さらにいくつかの質問が生じます。

  1. それらを一緒に検討する必要がありますか?

  2. これら2つの提案された手法の主な違いは何ですか?類似点は何ですか?

  3. 1つは他のより強力なバージョンですか?他の人ができないこと、またはその逆ができることはありますか?

  4. 一緒に使用できますか?これが必要になる状況はありますか?

  5. それらが一緒に使用される場合、例外はどのように処理されますか?両方のハンドラーを通過しますか?後者の場合、どれですか?

  6. それらが一緒に使用され、例外がスローされた場合 inside コントローラー(一方または他方)例外処理中、その例外はどのように処理されますか?他のコントローラーに送信されますか?例外は理論的にはコントローラー間でバウンスを開始したり、他の種類の非回復ループを作成したりできますか?

  7. Spring BootがSpringのResponseEntityExceptionHandlerを内部でどのように使用するか、またはSpring BootアプリケーションでSpring Bootがどのように使用されることを期待するかについて、信頼できる/公式のドキュメントはありますか?

  8. ResponseEntityExceptionHandlerだけですでに十分である場合、ErrorControllerが存在するのはなぜですか?

SpringのResponseEntityExceptionHandler@ExceptionHandlerアノテーションと一緒に見ると、さまざまなタイプの例外を個別に処理し、よりクリーンなコードを使用する方がより強力なようです。ただし、Spring BootはSpringの上に構築されているため、次のことを意味します。

  • Spring Bootを使用する場合、ErrorControllerを拡張する代わりにResponseEntityExceptionHandlerを実装する必要がありますか?
  • ErrorControllerは、さまざまな種類の例外を個別に処理することを含め、ResponseEntityExceptionHandlerでできるすべてのことを実行できますか?

編集:関連: Spring @ControllerAdvice vs ErrorController

8
anddero

Spring Bootアプリケーションには、エラー処理のためのデフォルト設定 ErrorMvcAutoConfiguration があります。

追加の構成が提供されていない場合の基本的な動作:

  • デフォルトのグローバルエラーコントローラーを作成します- BasicErrorController
  • デフォルトの「エラー」静的ビュー「ホワイトラベルエラーページ」を作成します。

BasicErrorControllerはデフォルトで「/ error」に接続されています。アプリケーションにカスタマイズされた「エラー」ビューがない場合、いずれかのコントローラーから例外がスローされた場合、ユーザーはBasicErrorControllerによって情報が入力された/ errorホワイトラベルページに移動します。

アプリケーションにErrorControllerを実装するコントローラーがある場合、それはreplacesBasicErrorControllerです。

エラー処理コントローラーで例外が発生した場合は、Spring例外フィルターを通過し(詳細は以下を参照)、最後に何も見つからない場合、この例外は、基になるアプリケーションコンテナー(例: Tomcat。基になるコンテナーは例外を処理し、その実装に応じていくつかのエラーページ/メッセージを表示します。

BasicErrorControllerjavadoc には興味深い情報があります。

ErrorAttributesをレンダリングする基本的なグローバルエラーコントローラー。より具体的なエラーは、Spring MVC抽象化(例:@ ExceptionHandler)を使用するか、サーブレットサーバーのエラーページを追加することで処理できます。

BasicErrorControllerまたはErrorControllerの実装はグローバルエラーハンドラーです。 @ExceptionHandlerと組み合わせて使用​​できます。

ここで ResponseEntityExceptionHandler にアクセスします

@ExceptionHandlerメソッドを介してすべての@RequestMappingメソッドにまたがる一元化された例外処理を提供したい@ControllerAdviceクラスの便利な基本クラス。この基本クラスは、Spring MVCの内部例外を処理するための@ExceptionHandlerメソッドを提供します。

言い換えると、これはResponseEntityExceptionHandlerが既に便利なクラスであり、Spring MVC例外処理がすでに含まれていることを意味します。そして、コントローラーの例外を処理するカスタムクラスの基本クラスとして使用できます。カスタムクラスを機能させるには、@ControllerAdviceで注釈を付ける必要があります。

@ControllerAdviceで注釈されたクラスは、グローバルエラーハンドラー(BasicErrorControllerまたはErrorController実装)と同時に使用できます。 @ControllerAdvice注釈付きクラス(ResponseEntityExceptionHandlerを拡張できない、または拡張できない)がいくつかの例外を処理しない場合、例外はグローバルエラーハンドラーに送られます。

これまでのところ、ErrorHandlerコントローラーと@ControllerAdviceで注釈されたものについて見てきました。しかし、それははるかに複雑です。私は質問で本当に貴重な洞察を見つけました- 複数の@ControllerAdvice @ExceptionHandlersの優先順位の設定

編集:

シンプルに保つには:

  1. 最初のSpringは、@ ControllerAdviceクラス内で例外ハンドラー(@ExceptionHandlerアノテーションが付けられたメソッド)を検索します。 ExceptionHandlerExceptionResolver を参照してください。
  2. 次に、スローされた例外に@ResponseStatusの注釈が付けられているか、ResponseStatusExceptionから派生しているかを確認します。 ResponseStatusExceptionResolver を参照してください。
  3. 次に、Spring MVC例外のデフォルトハンドラーを通過します。 DefaultHandlerExceptionResolver を参照してください。
  4. 最後に何も見つからなかった場合、コントロールはエラーページビューに転送され、その背後にグローバルエラーハンドラーが配置されます。例外がエラーハンドラ自体から発生した場合、このステップは実行されません。
  5. エラービューが見つからない場合(グローバルエラーハンドラーが無効になっている場合など)、または手順4がスキップされた場合、例外はコンテナーによって処理されます。
13
da-sha1

私はSpringのErrorControllerに過度に精通していないことを認めますが、指定された目標を見ると、Springの@ControllerAdviceを使用してそれらすべてをきれいに達成できると思います。以下は、私が自分のアプリケーションでそれをどのように使用しているかの例です。

@ControllerAdvice
public class ExceptionControllerAdvice {

    private static final String INCOMING_REQUEST_FAILED = "Incoming request failed:";
    private static final Logger LOGGER = LoggerFactory.getLogger(ExceptionControllerAdvice.class);
    private final MessageSource source;

    public ExceptionControllerAdvice2(final MessageSource messageSource) {
        source = messageSource;
    }

    @ExceptionHandler(value = {CustomException.class})
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ResponseBody
    public ErrorMessage badRequest(final CustomException ex) {
        LOGGER.error(INCOMING_REQUEST_FAILED, ex);
        final String message = source.getMessage("exception.BAD_REQUEST", null, LocaleContextHolder.getLocale());
        return new ErrorMessage(HttpStatus.BAD_REQUEST.value(), message);
    }

    @ExceptionHandler(Throwable.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ResponseBody
    public ErrorMessage internalServerError(final Exception ex) {
        LOGGER.error(INCOMING_REQUEST_FAILED, ex);
        final String message =
                source.getMessage("exception.INTERNAL_SERVER_ERROR", null, LocaleContextHolder.getLocale());
        return new ErrorMessage(HttpStatus.INTERNAL_SERVER_ERROR.value(), message);
    }
}
0
Zealot