Spring BootでTomcatのデフォルトエラーページが出るのを抑止する
Spring Boot を実行していると、Tomcatのデフォルトエラーページが表示されるような事象に遭遇することがあります。
このページのカスタマイズ方法は、上記issueにありますが、現状Spring Boot フレームワークでは提供されておらず少しトリッキーです。
まず ErrorReportValve
を継承したカスタムクラスを用意し、ここでhtmlを生成します。
import java.io.IOException;
import java.io.Writer;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Logger;
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response;
import org.apache.catalina.valves.ErrorReportValve;
import org.apache.coyote.ActionCode;
import org.apache.tomcat.util.ExceptionUtils;
public class CustomErrorReportValve extends ErrorReportValve {
// Create a simple logger
Logger log = Logger.getLogger(CustomErrorReportValve.class.getName());
@Override
protected void report(final Request request, final Response response, final Throwable throwable) {
// ErrorReportValve を参考に実装
final 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
final AtomicBoolean result = new AtomicBoolean(false);
response.getCoyoteResponse().action(ActionCode.IS_IO_ALLOWED, result);
if (!result.get()) {
return;
}
try {
try {
response.setContentType("text/html");
response.setCharacterEncoding("utf-8");
} catch (final Throwable t) {
ExceptionUtils.handleThrowable(t);
if (container.getLogger().isDebugEnabled()) {
container.getLogger().debug("status.setContentType", t);
}
}
final Writer writer = response.getReporter();
if (writer != null) {
// If writer is null, it's an indication that the response has
// been hard committed already, which should never happen
writer.write("<!doctype html><html lang=\"en\"><title>error</title><body>Error occured.</body></html>");
response.finishResponse();
}
} catch (IOException | IllegalStateException e) {
// Ignore
}
}
}
そしてこれを登録します。
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 MyConfig { // https://docs.spring.io/spring-boot/docs/2.4.4/reference/htmlsingle/#howto-use-tomcat-legacycookieprocessor // https://github.com/spring-projects/spring-boot/issues/21257#issuecomment-745565376 @Bean public WebServerFactoryCustomizer<TomcatServletWebServerFactory> errorReportValveCustomizer() { return (factory) -> { factory.addContextCustomizers(context -> { final Container parent = context.getParent(); if (parent instanceof StandardHost) { ((StandardHost) parent).setErrorReportValveClass( "com.example.errorcontrollerthrowexceptionexample.CustomErrorReportValve"); } }); }; } }
これで、元々 `ErrorReportValve ` で生成されていた デフォルトエラーページ の代わりに、自前で用意したページが表示されるようになります。