1. ホーム
  2. java

[解決済み] Spring Boot RESTサービスの例外処理

2022-05-10 19:10:28

質問

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

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

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

Node.jsでRESTアプリケーションを何年も書いてきた私にとっては、これは常識的なことではありません。 サービスエンドポイントが生成する例外は、すべてレスポンスで返されるべきです。 AngularやJQueryのSPAコンシューマーにリダイレクトを送るのは理解できませんが、それは答えを求めているだけで、リダイレクトに対して何もできない、あるいは何もしようとしません。

この例外は、リクエストマッピングメソッドから意図的に投げられるか、Springによって自動生成される(リクエストパスのシグネチャに対応するハンドラメソッドが見つからない場合は404)かのいずれかであり、MVCリダイレクトなしで標準フォーマットのエラー応答(400、500、503、404)をクライアントに返すことができるグローバルエラー処理装置を設定したい。 具体的には、エラーを受け取り、それをUUIDでNoSQLに記録し、JSONボディにログエントリのUUIDを含む正しいHTTPエラーコードをクライアントに返すつもりです。

この方法については、ドキュメントが曖昧になっています。 どうやら、自分で ErrorController の実装を使用するか コントローラアドバイス しかし、私が見たすべての例では、まだ何らかのエラーマッピングにレスポンスを転送しており、これは役に立ちません。 他の例では、単に "Throwable" をリストしてすべてを取得するのではなく、処理したいすべての Exception タイプをリストする必要があることを示唆しています。

このような場合、Node.jsの方が簡単に扱えるという連鎖を示唆することなく、私が何を見逃したのか、あるいは正しい方向性を示すことができる人はいますか?

どのように解決するのですか?

新しい回答 (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フレームワークによる自動設定を保持します。

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


オリジナルの回答

このサイトに投稿された解決策をさらに研究し、フォローアップし、Springコードの実行時トレースを少なからず行った結果、404を含むすべてのException(Errorではない、しかし続きを読む)を処理する設定をようやく見つけました。

ステップ 1 - ハンドラが見つからない場合、SpringBootにMVCを使用しないように指示します。 Springがクライアントに"/error"へのビューリダイレクトを返す代わりに、例外を投げるようにしたいのです。 これを行うには、設定クラスの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 を含むすべての例外を上記のハンドラクラスでキャッチするようになり、好きなように例外を処理できるようになりました。

最後にもうひとつ、throw Errorsをキャッチする方法はないようです。 私は、アスペクトを使用してエラーをキャッチし、それを例外に変えて上記のコードで処理するという奇抜なアイデアを持っていますが、実際にそれを実装してみる時間がまだありません。 これが誰かの助けになることを願っています。

コメント、修正、改善などありましたら、よろしくお願いします。