LoginSignup
12
10

More than 5 years have passed since last update.

Spring Boot DefaultErrorViewResolverを拡張してエラー画面を動的カスタマイズする

Posted at

やりたいこと

Spring Boot + Thymeleafでの画面開発において、何らかのエラーが発生した時にステータスコード(4xx、5xx等)に応じて表示するエラー画面をカスタマイズしたい。

Spring Boot Version

2.1.3.RELEASE

BasicErrorController

Spring Boot + Thymeleafでの画面開発は、エラーが発生した時用のエンドポイントが用意されている。エンドポイントとなるコントローラークラスは以下。

org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.java
@Controller
@RequestMapping("${server.error.path:${error.path:/error}}")
public class BasicErrorController extends AbstractErrorController {

@RequestMapping("${server.error.path:${error.path:/error}}") の記述にある通り、application.yaml(properties)に「server.error.path」でエラー画面のURLを記述しておけばそのURLがエンドポイントとなる。設定していなければデフォルトで「/error」がエンドポイントとなる(${error.path:/error}の記述の通り)。
そのため、開発者がエラー用コントローラーを作らなくても、以下のような構成でエラー画面HTMLを配置しておくだけでエラー画面の表示が可能となる。
d.PNG
http://localhost:8080/error -> 5xx.html
http://localhost:8080/error/404 -> 4xx.html

エラー画面を動的カスタマイズする

ただこの場合、用意されているエラーHTMLは静的なものであるため、例えば「セッションからログイン情報を取り出してエラー画面ヘッダ、フッタに表示する」「DBからある値を取り出してXXXしてエラー画面に表示する」といったように、動的にカスタマイズしたいという要望も出てくる。

そんな時は以下の要領で拡張してみる。
BasicErrorController#resolveErrorView(request, response, status, model)のところで、親クラスであるAbstractErrorControllerのresolveErrorViewを呼んでおり、その中のresolver.resolveErrorView(request, status, model)でErrorResolverのresolveErrorViewを呼んでいる。resolverはデフォルトで「org.springframework.boot.autoconfigure.web.servlet.errorDefaultErrorViewResolver」が利用されるため、このクラスをextendsしたResolver(ここではCustomeErrorViewResolverと命名)を作成し、resolveErrorViewメソッドをオーバーライドしてそこに動的カスタマイズしたModelAndViewを作成しreturnする。これだけで動的カスタマイズ可能となる。

org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.java
@RequestMapping(produces = MediaType.TEXT_HTML_VALUE)
public ModelAndView errorHtml(HttpServletRequest request,
        HttpServletResponse response) {
    HttpStatus status = getStatus(request);
    Map<String, Object> model = Collections.unmodifiableMap(getErrorAttributes(
            request, isIncludeStackTrace(request, MediaType.TEXT_HTML)));
    response.setStatus(status.value());
    ModelAndView modelAndView = resolveErrorView(request, response, status, model);
    return (modelAndView != null) ? modelAndView : new ModelAndView("error", model);
}
org.springframework.boot.autoconfigure.web.servlet.error.AbstractErrorController.java
protected ModelAndView resolveErrorView(HttpServletRequest request,
        HttpServletResponse response, HttpStatus status, Map<String, Object> model) {
    for (ErrorViewResolver resolver : this.errorViewResolvers) {
        ModelAndView modelAndView = resolver.resolveErrorView(request, status, model);
        if (modelAndView != null) {
            return modelAndView;
        }
    }
    return null;
}

以下のようなイメージ。

CustomeErrorViewResolver.java
@Component
public class CustomeErrorViewResolver extends DefaultErrorViewResolver {

    /**
     * Create a new {@link DefaultErrorViewResolver} instance.
     * @param applicationContext the source application context
     * @param resourceProperties resource properties
     */
    public CustomeErrorViewResolver(ApplicationContext applicationContext,ResourceProperties resourceProperties) {
        super(applicationContext, resourceProperties);
    }

    @Override
    public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status, Map<String, Object> model) {
        final ModelAndView mav = super.resolveErrorView(request, status, model);
        if (status.is4xxClientError()) {
            // 4XX系エラーの時の処理
       } else if (status.is5xxServerError()) {
            // 5XX系エラーの時の処理
        }
        return mav;
   }
}

以上です。

12
10
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
10