Spring Boot REST サービスの例外処理 質問する

Spring Boot REST サービスの例外処理 質問する

大規模な REST サービス サーバーをセットアップしようとしています。Spring Boot 1.2.1、Spring 4.1.5、Java 8 を使用しています。コントローラーは @RestController と標準の @RequestMapping アノテーションを実装しています。

私の問題は、Spring Boot がコントローラー例外のデフォルトのリダイレクトを に設定することです/error。ドキュメントから:

Spring Boot は、すべてのエラーを適切に処理する /error マッピングをデフォルトで提供し、サーブレット コンテナーに「グローバル」エラー ページとして登録されます。

Node.js で REST アプリケーションを何年も書いてきた私にとって、これはまったく理にかなったことではありません。サービス エンドポイントが生成する例外はすべて、応答で返される必要があります。おそらく Angular または JQuery SPA コンシューマーであるにもかかわらず、リダイレクトに対してアクションを実行できない、または実行しないユーザーにリダイレクトを送信する理由が理解できません。

私がやりたいのは、リクエスト マッピング メソッドから意図的にスローされた例外、または Spring によって自動生成された例外 (リクエスト パス シグネチャのハンドラー メソッドが見つからない場合は 404) のいずれかを取得できるグローバル エラー ハンドラーを設定し、MVC リダイレクトなしで標準形式のエラー応答 (400、500、503、404) をクライアントに返すことです。具体的には、エラーを取得して UUID とともに NoSQL に記録し、JSON 本文のログ エントリの UUID とともに正しい HTTP エラー コードをクライアントに返します。

ドキュメントでは、これを行う方法が曖昧です。独自のものを作成するか、エラーコントローラ実装または使用コントローラーアドバイス何らかの方法で、しかし私が見たすべての例には、応答を何らかのエラー マッピングに転送することが含まれており、これは役に立ちません。他の例では、単に「Throwable」をリストしてすべてを取得するのではなく、処理するすべての例外タイプをリストする必要があることが示唆されています。

私が見逃したものを誰か教えてもらえますか、または、Node.js の方が扱いやすいだろうというチェーン上の提案をせずに、これを行う方法について正しい方向を指し示してもらえますか?

ベストアンサー1

新しい回答 (2016-04-20)

Spring Boot 1.3.1.RELEASE の使用

新しいステップ1 -次のプロパティを application.properties に追加するのは簡単で、煩わしさも少なくなります。

spring.mvc.throw-exception-if-no-handler-found=true
spring.resources.add-mappings=false

既存の DispatcherServlet インスタンスを変更するよりもはるかに簡単です (以下のように)! - JO'

完全な RESTful アプリケーションで作業している場合、静的リソースの自動マッピングを無効にすることが非常に重要です。これは、静的リソースの処理に Spring Boot のデフォルト構成を使用している場合、リソース ハンドラーがリクエストを処理するため (最後に順序付けられ、/** にマップされるため、アプリケーション内の他のハンドラーによって処理されていないリクエストが取得される)、ディスパッチャー サーブレットが例外をスローする機会がなくなるためです。


新しい回答 (2015-12-04)

Spring Boot 1.2.7.RELEASE の使用

新しいステップ1 -「throExceptionIfNoHandlerFound」フラグを設定する、より邪魔にならない方法を見つけました。アプリケーション初期化クラスで、以下の DispatcherServlet 置換コード (手順 1) を次のコードに置き換えます。

@ComponentScan()
@EnableAutoConfiguration
public class MyApplication extends SpringBootServletInitializer {
    private static Logger LOG = LoggerFactory.getLogger(MyApplication.class);
    public static void main(String[] args) {
        ApplicationContext ctx = SpringApplication.run(MyApplication.class, args);
        DispatcherServlet dispatcherServlet = (DispatcherServlet)ctx.getBean("dispatcherServlet");
        dispatcherServlet.setThrowExceptionIfNoHandlerFound(true);
    }

この場合、既存の DispatcherServlet にフラグを設定しており、これにより Spring Boot フレームワークによる自動構成が保持されます。

もう 1 つ気づいたことがあります。@EnableWebMvc アノテーションは Spring Boot にとって致命的です。確かに、このアノテーションにより、以下で説明するようにすべてのコントローラー例外をキャッチできるようになりますが、Spring Boot が通常提供する便利な自動構成の多くも無効になります。Spring Boot を使用するときは、このアノテーションを細心の注意を払って使用してください。


元の回答:

さらに調査を重ね、ここに投稿された解決策をフォローアップし (ご協力ありがとうございます)、Spring コードの実行時トレースをかなり行った結果、404 を含むすべての例外 (エラーではありませんが、読み進めてください) を処理する構成をようやく見つけました。

ステップ1 -「ハンドラが見つからない」状況で MVC の使用を停止するように SpringBoot に指示します。クライアントに「/error」へのビュー リダイレクトを返す代わりに、Spring が例外をスローするようにします。これを行うには、構成クラスの 1 つにエントリが必要です。

// NEW CODE ABOVE REPLACES THIS! (2015-12-04)
@Configuration
public class MyAppConfig {
    @Bean  // Magic entry 
    public DispatcherServlet dispatcherServlet() {
        DispatcherServlet ds = new DispatcherServlet();
        ds.setThrowExceptionIfNoHandlerFound(true);
        return ds;
    }
}

この方法の欠点は、デフォルトのディスパッチャ サーブレットが置き換えられることです。これは今のところ問題にはなっておらず、副作用や実行上の問題は発生していません。他の理由でディスパッチャ サーブレットを使用して何か他の操作を行う場合は、ここで実行してください。

ステップ2 -ハンドラーが見つからない場合に Spring Boot が例外をスローするようになったので、その例外は統合された例外ハンドラー内の他の例外と共に処理できます。

@EnableWebMvc
@ControllerAdvice
public class ServiceExceptionHandler extends ResponseEntityExceptionHandler {

    @ExceptionHandler(Throwable.class)
    @ResponseBody
    ResponseEntity<Object> handleControllerException(HttpServletRequest req, Throwable ex) {
        ErrorResponse errorResponse = new ErrorResponse(ex);
        if(ex instanceof ServiceException) {
            errorResponse.setDetails(((ServiceException)ex).getDetails());
        }
        if(ex instanceof ServiceHttpException) {
            return new ResponseEntity<Object>(errorResponse,((ServiceHttpException)ex).getStatus());
        } else {
            return new ResponseEntity<Object>(errorResponse,HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }

    @Override
    protected ResponseEntity<Object> handleNoHandlerFoundException(NoHandlerFoundException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
        Map<String,String> responseBody = new HashMap<>();
        responseBody.put("path",request.getContextPath());
        responseBody.put("message","The URL you have reached is not in service at this time (404).");
        return new ResponseEntity<Object>(responseBody,HttpStatus.NOT_FOUND);
    }
    ...
}

ここで「@EnableWebMvc」アノテーションが重要だと考えていることに注意してください。このアノテーションがないと何も機能しないようです。これで完了です。これで、Spring Boot アプリは上記のハンドラー クラスで 404 を含むすべての例外をキャッチし、必要に応じて処理できるようになります。

最後にもう 1 つ、スローされたエラーをキャッチする方法はないようです。アスペクトを使用してエラーをキャッチし、それを例外に変換して上記のコードで処理できるようにするという奇抜なアイデアがありますが、実際に実装してみる時間はまだありません。

コメント、訂正、機能強化などありましたら、ぜひお知らせください。

おすすめ記事