LoginSignup
3
4

Spring BootでTomcatデフォルトのエラーページをカスタマイズする

Last updated at Posted at 2024-04-11

環境

  • JDK 21
  • Spring Boot 3.2.4
  • Embedded Tomcat 10.1.19

やりたいこと

Spring Bootにもエラーページの機能があります。具体的にはsrc/main/resources/templates直下にerror.html、src/main/resources/templates/error直下に404.html・500.htmlなどを作成すればOKです(公式ドキュメント)。

しかし、サーブレットフィルターのレベルで例外がスローされると、前述のエラーページではなくTomcatデフォルトのエラーページが表示されてしまいます。

スクリーンショット 2024-04-11 16.11.02.png

これを自作のエラーページに差し替えたいです。

自作エラーページの作成

静的なHTMLを作成して、適当な場所・適当なファイル名で配置します。今回はsrc/main/resources/templates/tomcatフォルダにtomcat-error.htmlという名前で配置することにします。

ErrorReportValveのカスタマイズ

Tomcatデフォルトのエラーページをレスポンスしているのが、TomcatのErrorReportValveクラスです。これを継承+オーバーライドすることで、作成した自作エラーページをレスポンスするよう変更します。

CustomErrorReportValve.java
package com.example;

import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response;
import org.apache.catalina.util.IOTools;
import org.apache.catalina.valves.ErrorReportValve;
import org.apache.coyote.ActionCode;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UncheckedIOException;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * Tomcatエラーページをカスタマイズします。
 */
public class CustomErrorReportValve extends ErrorReportValve {

    /**
     * カスタマイズしたエラーページをレスポンスします。
     * @see ErrorReportValve#report(Request, Response, Throwable)
     * @param request   The request being processed
     * @param response  The response being generated
     * @param throwable The exception that occurred (which possibly wraps a root cause exception
     */
    @Override
    protected void report(Request request, Response response, Throwable throwable) {
        int statusCode = response.getStatus();

        // Do nothing on a 1xx, 2xx and 3xx status
        // Do nothing if anything has been written already
        // Do nothing if the response hasn't been explicitly marked as in error
        // and that error has not been reported.
        if (statusCode < 400 || response.getContentWritten() > 0 || !response.setErrorReported()) {
            return;
        }

        // If an error has occurred that prevents further I/O, don't waste time
        // producing an error report that will never be read
        AtomicBoolean result = new AtomicBoolean(false);
        response.getCoyoteResponse().action(ActionCode.IS_IO_ALLOWED, result);
        if (!result.get()) {
            return;
        }
        // ここまでのコード👆は、ErrorReportValve#report()からコピーしました

        // カスタマイズしたエラーページをレスポンス
        response.setContentType("text/html");
        response.setCharacterEncoding("UTF-8");
        try (OutputStream os = response.getOutputStream();
             InputStream is = this.getClass().getResourceAsStream("/templates/tomcat/tomcat-error.html")) {
            IOTools.flow(is, os);
        } catch (IOException e) {
            throw new UncheckedIOException(e);
        }
    }
}

TomcatがCustomErrorReportValveを使うように設定

ServerConfig.java
package com.example;

import org.apache.catalina.Container;
import org.apache.catalina.core.StandardHost;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * 組み込みサーバーに関する設定です。
 */
@Configuration
public class ServerConfig {

    /**
     * Tomcatのエラー画面を自作のものに変更します。
     */
    @Bean
    public WebServerFactoryCustomizer<TomcatServletWebServerFactory> webServerFactoryCustomizer() {
        return factory -> factory.addContextCustomizers(context -> {
            Container parent = context.getParent();
            if (parent instanceof StandardHost host) {
                host.setErrorReportValveClass(CustomErrorReportValve.class.getName());
            }
        });
    }
}

参考URL

3
4
0

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
3
4