web-dev-qa-db-ja.com

Spring MVCでビューをレンダリングしているときにスローされた例外を処理するにはどうすればよいですか?

FreeMarkerをビュー技術として使用するSpring MVCアプリケーションがあります(ただし、ビュー技術は私の質問にはあまり関係ありません)。リクエスト中にスローされる可能性のあるすべての例外をインターセプトする必要があります。

HandlerExceptionResolver を実装しましたが、このリゾルバーは、コントローラー内で例外が発生したときにのみ実行されます。しかし、コントローラーがModelAndViewを返し、ビューのレンダリング中に例外が発生すると(変数が見つからなかったか、このようなものだったため)、例外リゾルバーは呼び出されず、代わりにブラウザーウィンドウにスタックトレースが表示されます。

私はまた、ビューを返し、@ ExceptionHandlerで注釈を付けたコントローラー内の例外ハンドラーメソッドを使用しようとしましたが、これも機能しません(例外はコントローラーではなくビューでスローされるため、おそらく再度発生します)。

ビューのエラーをキャプチャする例外ハンドラを登録できるSpringメカニズムはありますか?

27
kayahr

Wordの前払い:ロジックやモデルの準備をせずに「静的」エラーページだけが必要な場合は、<error-page>- Tagをweb.xmlに挿入するだけで十分です(例については以下を参照)。

そうでなければ、これを行うにはより良い方法があるかもしれませんが、これは私たちにとってはうまくいきます:

Spring HandlerExceptionResolver内で使用するのと同じように、<filter>内のサーブレットweb.xmlを使用して、すべての例外をキャッチし、カスタムErrorHandlerを呼び出します。

<filter>
   <filter-name>errorHandlerFilter</filter-name>
   <filter-class>org.example.filter.ErrorHandlerFilter</filter-class>
</filter>
<filter-mapping>
  <filter-name>errorHandlerFilter</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>

実装は基本的に次のようになります。

public class ErrorHandlerFilter implements Filter {

  ErrorHandler errorHandler;

  @Override
  public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
    try {
      filterChain.doFilter(request, response);
    } catch (Exception ex) {
      // call ErrorHandler and dispatch to error jsp
      String errorMessage = errorHandler.handle(request, response, ex);
      request.setAttribute("errorMessage", errorMessage);
      request.getRequestDispatcher("/WEB-INF/jsp/error/dispatch-error.jsp").forward(request, response);
    }

  @Override
  public void init(FilterConfig filterConfig) throws ServletException {
    errorHandler = (ErrorHandler) WebApplicationContextUtils
      .getRequiredWebApplicationContext(filterConfig.getServletContext())
      .getBean("defaultErrorHandler");
  }

  // ...
}

これはFreeMarkerテンプレートでもほぼ同じように機能するはずです。もちろん、エラービューがエラーをスローする場合、多かれ少なかれ選択肢はありません。

また、404などのエラーをキャッチしてモデルを準備するために、ERRORディスパッチャーにマップされているフィルターを使用します。

<filter>
   <filter-name>errorDispatcherFilter</filter-name>
   <filter-class>org.example.filter.ErrorDispatcherFilter</filter-class>
</filter>
<filter-mapping>
  <filter-name>errorDispatcherFilter</filter-name>
  <url-pattern>/*</url-pattern>
  <dispatcher>ERROR</dispatcher>
</filter-mapping>

<error-page>
  <error-code>404</error-code>
  <location>/WEB-INF/jsp/error/dispatch-error.jsp</location>
</error-page>
<error-page>
  <exception-type>Java.lang.Exception</exception-type>
  <location>/WEB-INF/jsp/error/dispatch-error.jsp</location>
</error-page>

DoFilter-Implementationは次のようになります。

@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

  final HttpServletRequest request = (HttpServletRequest) servletRequest;

  // handle code(s)
  final int code = (Integer) request.getAttribute("javax.servlet.error.status_code");
  if (code == 404) {
    final String uri = (String) request.getAttribute("javax.servlet.error.request_uri");
    request.setAttribute("errorMessage", "The requested page '" + uri + "' could not be found.");
  }

  // notify chain
  filterChain.doFilter(servletRequest, servletResponse);
}
21
oxc

DispatcherServletを拡張できます。

Web.xmlで、独自のクラスの汎用DispatcherServletを置き換えます。

<servlet>
    <servlet-name>springmvc</servlet-name>
    <servlet-class>com.controller.generic.DispatcherServletHandler</servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>

後で独自のクラスDispatcherServletHandlerを作成し、DispatcherServletから拡張します。

public class DispatcherServletHandler extends DispatcherServlet {

    private static final String ERROR = "error";
    private static final String VIEW_ERROR_PAGE = "/WEB-INF/views/error/view-error.jsp";

    @Override
    protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
        try{
            super.doService(request, response);
        } catch(Exception ex) {
            request.setAttribute(ERROR, ex);
            request.getRequestDispatcher(VIEW_ERROR_PAGE).forward(request, response);
        }
     }
}

そのページでは、ユーザーにメッセージを表示するだけです。

7
cralfaro

私の解決策があなたが抱えている問題で機能するかどうかわかりません。例外をキャッチする方法を投稿して、ブラウザ内にスタックトレースが表示されないようにします。

次のような特定の競合を処理するメソッドでAbstractControllerクラスを作成しました。

public class AbstractController {

    @ResponseStatus(HttpStatus.CONFLICT)
    @ExceptionHandler({OptimisticLockingFailureException.class})
    @ResponseBody
    public void handleConflict() {
    //Do something extra if you want
    }
}

これにより、例外が発生するたびに、デフォルトのHTTPResponseステータスが表示されます。 (例:404 Not Foundなど)

すべてのコントローラークラスでこのクラスを拡張して、エラーがAbstractControllerにリダイレクトされるようにします。この方法では、特定のコントローラーでExceptionHandlerを使用する必要はありませんが、すべてのコントローラーにグローバルに追加できます。 (AbstractControllerクラスを拡張することにより)。

編集:もう一度質問したところ、ビューにエラーが発生していることに気付きました。この方法でそのエラーをキャッチできるかどうかはわかりません。

お役に立てれば!!

0
Byron Voorbach