JSP + spring MVCを使っている。web.xmlもない。
私の問題
jspがdto.getFiled()を呼んでいるが、dto.getFiled()の中身で例外を投げる可能性があるコードがあった。
例えば
public class MyDto {
private String field;
public String getFiled() {
if (field == null) {
throw new NullPointerException();
}
return field;
}
}
JSPの描画中に例外が発生しても、SpringのGlobalHandlerでは検知できなかった。
共通エラーページに遷移するのが正しいが、遷移しなく、JSPの描画が途中で止まり、以降の内容が空白になったり、白画面になることがあった。
理由は?
SpringのExceptionHandlerはDispatcherServletの管理下にあるControllerで発生した例外を処理する。
JSPの例外はSpringのDispatcherServletに到達しない。
(JSPはServletコンテナ(Tomcat)がレンダリングするため)
私のGlobalExceptionHandler
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
public ModelAndView handleException(Exception ex) {
ModelAndView modelAndView = new ModelAndView("errorPage"); // errorPage.jsp
modelAndView.addObject("error", ex.getMessage());
return modelAndView;
}
}
解決方法
JSPで発生する例外を検知するため、独自のFilterを作成することにした。
なぜfilterか?
FilterとInterceptorでは、検知できる例外の範囲が異なるため、用途に応じて使い分ける必要がある。
InterceptorはSpringのController処理に限定されており、JSP描画中の例外は拾えない。
一方、FilterはServlet全体をカバーできるため、JSP内部で発生する例外も検知できる。
そのため、JSPでの例外処理にはFilterの方が適している。
Filterで例外をキャッチする
JSPの描画中に発生した例外は、SpringのExceptionHandlerでは拾えないため、Filterを使って検知する方法を選んだ。
以下のように、Filterの中でchain.doFilter()をtry-catchで囲むことで、JSP中のNullPointerExceptionなどを検知できる。
@Component
@Order(1)
public class ExceptionCatchingFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
try {
chain.doFilter(request, response);
} catch (Exception e) {
//ここに内容記載(エラーページに遷移するとか、レスポンスをリセットするとか)
}
}
}
まとめ
JSPで発生する例外はControllerの外側で起きるため、Filterでの対応が有効。