WEBアプリケーションを作成する際、処理の途中でエラーが発生した場合に、特定のエラーページに遷移してエラーメッセージを表示したり、エラーログを出力したりする必要がある。
Spring BootのWEBアプリケーションにおいては、処理の途中で例外が発生した場合は、何もしなくても「resources/templates/error.html」に画面遷移するようになっている。また、RequestDispatcherFilterやErrorControllerによって、エラー時の画面遷移先を変えたり、エラーメッセージの文言を変えたり、エラーログの出力機能を追加したりすることもできる。
今回は、Filter内でエラーが発生した場合のサンプルプログラムを通して、Spring Bootの例外処理について共有する。Spring Bootデフォルトのエラー処理・RequestDispatcherを利用したエラー処理・ErrorControllerを利用したエラー処理の3種類について共有する。
前提条件
以下の記事のSpring BootのWEB画面へのフィルタ追加が完了していること。
やってみたこと
Spring Bootデフォルトのエラー処理
今回は、フィルタ処理内(DemoFilter2.java)でエラーを発生させ、エラーログを出力後、エラー画面(error.html)に遷移するサンプルプログラムを作成した。
また、今回変更したプログラムの内容は以下の通り。DemoFilter2.javaでは、エラーログを出力後に発生した例外をそのままスローしているだけとなっているが、これだけで「error.html」に遷移するようになっている。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | package com.example.demo; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import javax.servlet.Filter; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.FilterChain; import javax.servlet.ServletException; import java.io.IOException; public class DemoFilter2 implements Filter { //ログ出力のためのクラス private static Log log = LogFactory.getLog(DemoFilter2.class); /** * 処理(本プログラムではコントローラクラスのメソッド)の前後にログ出力 * を行うフィルタ定義 * @param request サーブレットリクエスト * @param response サーブレットレスポンス * @param chain フィルタチェイン * @throws IOException * @throws ServletException */ @Override public void doFilter(ServletRequest request, ServletResponse response , FilterChain chain) throws IOException, ServletException { log.debug("DemoFilter2 started."); try{ //ここでArithmeticExceptionを強制的に発生させる int i = 1 / 0; }catch(Exception e){ //エラーログを出力 log.error(e.toString()); //例外をスローすると、resources/templates/error.htmlに遷移 throw e; } //一連の処理(本プログラムではコントローラクラスのメソッド)を実行 chain.doFilter(request, response); log.debug("DemoFilter2 ended."); } } |
1 2 3 4 5 6 7 8 9 10 | <!DOCTYPE html> <html lang="ja"> <meta charset="UTF-8"> <title>error page</title> </head> <body> Error Occured!! <p>システムエラーが発生しました、ログを確認してください。</p> </body> </html> |
その他のソースコード内容は、以下のサイトを参照のこと。
https://github.com/purin-it/java/tree/master/spring-boot-filter2/default/demo
その後、Spring Bootアプリケーションを起動し、「http:// (ホスト名):(ポート番号)」とアクセスした結果は以下の通りで、エラー画面(error.html)に遷移する。
また、その際のコンソール・ログ出力結果は以下の通りで、下図赤枠部分で、「DemoFilter2.java」の開始ログ出力後に、エラーログが出力されているのが確認できる。
●ログファイル(C:/work/logs/demo.log)
RequestDispatcherによるエラー処理
今回は、RequestDispatcherによってエラー画面に遷移するコントローラのメソッドを、パラメータを付与した形で呼び出すサンプルプログラムを作成した。
また、今回変更したプログラムの内容は以下の通り。DemoFilter2.javaでは、エラーログを出力後に、RequestDispatcherによって、「/error_filter」のパス(DemoController.javaに記載)に遷移するようになっている。また、その際にexceptionというパラメータを渡すようにしていて、画面上に発生したエラー内容が表示できるようになっている。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | package com.example.demo; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import javax.servlet.Filter; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.RequestDispatcher; import java.io.IOException; public class DemoFilter2 implements Filter { //ログ出力のためのクラス private static Log log = LogFactory.getLog(DemoFilter2.class); /** * 処理(本プログラムではコントローラクラスのメソッド)の前後にログ出力を行うフィルタ定義 * @param request サーブレットリクエスト * @param response サーブレットレスポンス * @param chain フィルタチェイン * @throws IOException * @throws ServletException */ @Override public void doFilter(ServletRequest request, ServletResponse response , FilterChain chain) throws IOException, ServletException { log.debug("DemoFilter2 started."); try{ //ここでArithmeticExceptionを強制的に発生させる int i = 1 / 0; }catch(Exception e){ //エラーログを出力 log.error(e.toString()); StackTraceElement[] errors = e.getStackTrace(); for(StackTraceElement element : errors){ log.error(element); } //RequestMappingが「/error_filter」であるコントローラのメソッド //(DemoController.java、errorFilter)を呼び出す RequestDispatcher rd = request.getRequestDispatcher( "/error_filter?exception=" + e.toString()); rd.forward(request, response); return; } //一連の処理(本プログラムではコントローラクラスのメソッド)を実行 chain.doFilter(request, response); log.debug("DemoFilter2 ended."); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | package com.example.demo; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.servlet.ModelAndView; @Controller public class DemoController { /** * 初期表示を行う * @return 初期表示画面のパス */ @RequestMapping("/") public String index(){ return "index"; } /** * フィルタ内でエラーが発生した場合の画面遷移を行う * @param exception パラメータとして渡された例外 * @param mav ModelAndViewオブジェクト * @return ModelAndViewオブジェクト */ @RequestMapping("/error_filter") public ModelAndView errorFilter(@RequestParam("exception") String exception , ModelAndView mav){ //resources/templates下のerror.htmlに遷移 mav.setViewName("error"); //error.htmlに埋め込むパラメータ「errClass」に、 //パラメータとして渡された例外を埋め込む mav.addObject("errClass", exception); return mav; } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 | <!DOCTYPE html> <html lang="ja" xmlns:th="http://www.thymeleaf.org"> <meta charset="UTF-8"> <title>error page</title> </head> <body> Error Occured!! <p>エラーが発生しました、ログを確認してください。</p> <p th:text="'発生したエラー: ' + ${errClass}"> ここに発生したエラーのクラス名が設定されます </p> </body> </html> |
その他のソースコード内容は、以下のサイトを参照のこと。
https://github.com/purin-it/java/tree/master/spring-boot-filter2/requestdispatcher/demo
その後、Spring Bootアプリケーションを起動し、「http:// (ホスト名):(ポート番号)」とアクセスした結果は以下の通りで、エラー画面(error.html)に遷移し、パラメータで渡したエラー内容が表示されることが確認できる。
また、その際のコンソール・ログ出力結果は以下の通りで、下図赤枠部分で、「DemoFilter2.java」の開始ログ出力後に、エラーログが出力されているのが確認できる。
●ログファイル(C:/work/logs/demo.log)
ErrorControllerによるエラー処理
今回は、フィルタ処理内(DemoFilter2.java)でエラーを発生させた後で、ErrorControllerクラスをimplementsしたクラス内(DemoExceptionController.java)で、エラーログの出力とエラー画面(error_filter.html)への遷移を行うサンプルプログラムを作成した。
また、今回変更したプログラムの内容は以下の通り。DemoFilter2.javaでは、エラーログを出力後にRuntimeException例外をスローしていて、これがruntimeExceptionHandlerメソッド(DemoExceptionController.javaに記載)で処理できるようになっている。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | package com.example.demo; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import javax.servlet.Filter; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.FilterChain; import javax.servlet.ServletException; import java.io.IOException; public class DemoFilter2 implements Filter { //ログ出力のためのクラス private static Log log = LogFactory.getLog(DemoFilter2.class); /** * 処理(本プログラムではコントローラクラスのメソッド)の前後にログ出力を行うフィルタ定義 * @param request サーブレットリクエスト * @param response サーブレットレスポンス * @param chain フィルタチェイン * @throws IOException * @throws ServletException */ @Override public void doFilter(ServletRequest request, ServletResponse response , FilterChain chain) throws IOException, ServletException { log.debug("DemoFilter2 started."); try{ //ここでArithmeticExceptionを強制的に発生させる int i = 1 / 0; }catch(Exception e){ throw new RuntimeException(e); } //一連の処理(本プログラムではコントローラクラスのメソッド)を実行 chain.doFilter(request, response); log.debug("DemoFilter2 ended."); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | package com.example.demo; import org.springframework.boot.web.servlet.error.ErrorController; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.servlet.ModelAndView; /** * RuntimeException発生時の処理を実装 * 通常、SpringBootアプリケーションでエラーが発生した場合は resources/templates/error.html に * 遷移するようになっているが、その遷移先を変えたり、文言追加オブジェクト等を追加するには、 * このクラスのように、ErrorControllerクラスをimplementsしたクラスを作成する */ @Controller public class DemoExceptionController implements ErrorController { /** * エラーが発生した場合の画面遷移 * @param e RuntimeException例外 * @param mav ModelAndViewオブジェクト * @return ModelAndViewオブジェクト */ @RequestMapping("/error") @ExceptionHandler(RuntimeException.class) public ModelAndView runtimeExceptionHandler(RuntimeException e, ModelAndView mav){ mav.setViewName("error_filter"); mav.addObject("errMsg", "システムエラーが発生しました、ログを確認してください。"); return mav; } /** * エラーパスを取得 * (このメソッドを追加しないとコンパイルエラーになるため追加) * @return エラーパス */ @Override public String getErrorPath() { return ""; } } |
1 2 3 4 5 6 7 8 9 10 | <!DOCTYPE html> <html lang="ja" xmlns:th="http://www.thymeleaf.org"> <meta charset="UTF-8"> <title>error page</title> </head> <body> Error Occured!! <p th:text="${errMsg}">ここに発生したエラーメッセージが設定されます</p> </body> </html> |
その他のソースコード内容は、以下のサイトを参照のこと。
https://github.com/purin-it/java/tree/master/spring-boot-filter2/errorcontroller/demo
その後、Spring Bootアプリケーションを起動し、「http:// (ホスト名):(ポート番号)」とアクセスした結果は以下の通りで、エラー画面(error_filter.html)に遷移する。
また、その際のコンソール・ログ出力結果は以下の通りで、下図赤枠部分で、「DemoFilter2.java」の開始ログ出力後に、エラーログが出力されているのが確認できる。
●ログファイル(C:/work/logs/demo.log)
要点まとめ
- Spring BootのWEBアプリケーションにおいては、処理の途中で例外が発生した場合は、何もしなくても「resources/templates/error.html」に画面遷移するようになっている。
- RequestDispatcherクラスのオブジェクトでforwardすることによって、例外処理を行うメソッドに遷移することができる。
- ErrorControllerをimplementsしたクラスを利用した例外処理も行える。