web-dev-qa-db-ja.com

Spring MVC / Spring Bootで適切なグローバルエラーハンドラーを書く方法

Tomcatを埋め込みWebコンテナとして使用して、Spring 4.0.4およびSpring Boot 1.0.2でWebアプリケーションを作成しています。すべての例外をインターセプトし、特定の方法でログに記録するグローバルな例外処理を実装します。私の簡単な要件は次のとおりです。

  • 他の場所で処理されていないすべての例外をグローバルに処理したい(たとえば、コントローラー例外ハンドラーで)。メッセージをログに記録し、ユーザーにカスタムエラーメッセージを表示します。
  • 私はこれを自分でやりたいので、SpringまたはWebコンテナがそれ自体でエラーをログに記録したくないのです。

これまでのところ、私の解決策は次のようになります(単純化され、ログ記録もエラービューへのリダイレクトもない)。

@Controller
@RequestMapping("/errors")
public class ErrorHandler implements EmbeddedServletContainerCustomizer
{
    @Override
    public void customize(final ConfigurableEmbeddedServletContainer factory)
    {
        factory.addErrorPages(new ErrorPage("/errors/unexpected"));
        factory.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND, "/errors/notfound"));
    }

    @RequestMapping("unexpected")
    @ResponseBody
    public String unexpectedError(final HttpServletRequest request)
    {
        return "Exception: " + request.getAttribute("javax.servlet.error.exception");
    }

    @RequestMapping("notfound")
    @ResponseBody
    public String notFound()
    {
        return "Error 404";
    }
}

その結果、コントローラーでスローされた例外はunexpectedErrorメソッドによって正しく処理され、404ステータスコードはnotFoundメソッドによって処理されます。これまでのところは良いですが、次の問題があります。

  • TomcatまたはSpring(誰が責任を負うかは不明)は、引き続きエラーメッセージを記録しています。自分で(追加情報を使用して)ログに記録し、ログにエラーメッセージが重複して表示されないようにするため、これは望ましくありません。このデフォルトのロギングを防ぐにはどうすればよいですか?
  • 例外オブジェクトにアクセスする方法は正しくないようです。リクエスト属性javax.servlet.error.exceptionからifを取得します。そして、それはスローされた例外でさえありません。それはorg.springframework.web.util.NestedServletExceptionのインスタンスであり、実際の例外を取得するにはこのネストされた例外に飛び込む必要があります。もっと簡単な方法があると確信していますが、見つけることができません。

これらの問題をどのように解決できますか?それとも、このグローバル例外ハンドラを実装した方法が完全に間違っており、より良い代替手段がありますか?

37
kayahr

ControllerAdviceをご覧ください次のようなことができます:

@ControllerAdvice
public class ExceptionHandlerController {

    public static final String DEFAULT_ERROR_VIEW = "error";

    @ExceptionHandler(value = {Exception.class, RuntimeException.class})
    public ModelAndView defaultErrorHandler(HttpServletRequest request, Exception e) {
            ModelAndView mav = new ModelAndView(DEFAULT_ERROR_VIEW);

        mav.addObject("datetime", new Date());
        mav.addObject("exception", e);
        mav.addObject("url", request.getRequestURL());
        return mav;
    }
}
55
Michiel