[解決済み] Spring Boot RESTサービスの例外処理
質問
大規模な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をキャッチする方法はないようです。 私は、アスペクトを使用してエラーをキャッチし、それを例外に変えて上記のコードで処理するという奇抜なアイデアを持っていますが、実際にそれを実装してみる時間がまだありません。 これが誰かの助けになることを願っています。
コメント、修正、改善などありましたら、よろしくお願いします。
関連
-
eclipse の実行時に java 仮想マシンが見つからなかった
-
java.lang.NoClassDefFoundError: org.apache.jasper.el.ELContextImpl クラスを初期化できませんでした。
-
スレッド "main" で例外発生 java.net.BindException: アドレスは既に使用中です。NET_Bind
-
起動時にEclipseエラーが発生しました。起動中に内部エラーが発生しました。java.lang.NullPoin: "Javaツーリングの初期化 "中に内部エラーが発生しました。
-
Java(1)仕上げの基本概念+eclipseのインストール構成
-
maven プラグイン エラー プラグインの実行は、ライフサイクル構成ソリューションの対象外です。
-
[解決済み] JUnit 4のテストで、ある例外が投げられたことをどのように断言しますか?
-
[解決済み] SOAPとRESTの比較(相違点)
-
[解決済み] Spring Bootアプリケーションにポートを設定する方法
-
[解決済み] Node.jsの例外処理のベストプラクティス
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
プロジェクトの依存関係を解決できなかった 解決
-
JQuery DataTable 详解
-
eclipseにプロジェクトをインポートした後、Editorにmain typeが含まれない問題
-
node js npm gruntインストール、elasticsearch-head 5.Xインストール
-
javaコンパイル時のエラー:不正な文字 '\ufeff' に対する解決策です。
-
が 'X-Frame-Options' を 'deny' に設定しているため、フレーム内にある。
-
javaでクラスを作成すると、enclosing classでないように見える
-
Error: java.lang.NoClassDefFoundError: クラス XXXX を初期化できませんでした
-
IDEA パッケージステートメントの欠落
-
Google Chromeのエラー「Not allowed to load local resource」の解決策について