spring boot-related configuration 404 pages are for (if configured context-path) under the project path
In the case of context-path is not empty, if the access path without a context-path, this time will display a blank page or a tomcat default 404 page
How this time custom built tomcat 404 pages?
View tomcat error pages to realize source org.apache.catalina.valves.ErrorReportValue:
report method first checks whether the error page is registered, not registered any default error page, then send an error page by sendErrorPage method
private boolean sendErrorPage(String location, Response response) { File file = new File(location); if (!file.isAbsolute()) { file = new File(getContainer().getCatalinaBase(), location); } if (!file.isFile() || !file.canRead()) { getContainer().getLogger().warn( sm.getString("errorReportValve.errorPageNotFound", location)); return false; } // Hard coded for now. Consider making this optional. At Valve level or // page level? response.setContentType("text/html"); response.setCharacterEncoding("UTF-8"); try (OutputStream os = response.getOutputStream(); InputStream is = new FileInputStream(file);){ IOTools.flow(is, os); } catch (IOException e) { getContainer().getLogger().warn( sm.getString("errorReportValve.errorPageIOException", location), e); return false; } return true; }
Since spring boot default labeled jar package running tomcat, it is necessary to take 404 pages to the outside, where the first 404.html put under the resource directory, then start the replication process will tomcat page to a temporary directory, the path to the 404 the page on it.
There are two ways to achieve:
1, modify the default registered ErrorReportValue by AOP
import java.io.File; import java.io.IOException; import javax.servlet.Servlet; import org.apache.catalina.startup.Tomcat; import org.apache.catalina.valves.ErrorReportValve; import org.apache.coyote.UpgradeProtocol; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.web.embedded.TomcatWebServerFactoryCustomizer; import org.springframework.boot.web.embedded.tomcat.ConfigurableTomcatWebServerFactory; import org.springframework.core.io.ClassPathResource; import org.springframework.stereotype.Component; import org.springframework.util.FileCopyUtils; import com.bc.core.util.FileUtil; @Aspect @ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class, TomcatWebServerFactoryCustomizer.class }) @Component public class TomcatCustomizerAspect { @Pointcut("execution(public void org.springframework.boot.autoconfigure.web.embedded.TomcatWebServerFactoryCustomizer.customize(*))") public void customize() { } @After(value = "customize()") public void doAfter(JoinPoint joinPoint) throws Throwable { if (!(joinPoint.getArgs()[0] instanceof ConfigurableTomcatWebServerFactory)) { return; } ConfigurableTomcatWebServerFactory factory = (ConfigurableTomcatWebServerFactory) joinPoint.getArgs()[0]; addTomcat404CodePage(factory); } private static void addTomcat404CodePage(ConfigurableTomcatWebServerFactory factory) { factory.addContextCustomizers((context) -> { String path = context.getCatalinaBase().getPath() + "/404.html"; ClassPathResource cr = new ClassPathResource("404.html"); if (cr.exists()) { File file404 = new File(path); if (!file404.exists()) { try { FileCopyUtils.copy(cr.getInputStream(), FileUtil.openOutputStream(file404)); } catch (IOException e) { e.printStackTrace(); } } } ErrorReportValve valve = new ErrorReportValve(); valve.setProperty("errorCode.404", path); valve.setShowServerInfo(false); valve.setShowReport(false); valve.setAsyncSupported(true); context.getParent().getPipeline().addValve(valve); }); } }
2, by adding a custom BeanPostProcessor custom ErrorReportValve
import java.io.File; import java.io.IOException; import javax.servlet.Servlet; import org.apache.catalina.startup.Tomcat; import org.apache.catalina.valves.ErrorReportValve; import org.apache.coyote.UpgradeProtocol; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.web.embedded.TomcatWebServerFactoryCustomizer; import org.springframework.boot.web.embedded.tomcat.ConfigurableTomcatWebServerFactory; import org.springframework.core.io.ClassPathResource; import org.springframework.stereotype.Component; import org.springframework.util.FileCopyUtils; import com.bc.core.util.FileUtil; import lombok.extern.slf4j.Slf4j; @ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class, TomcatWebServerFactoryCustomizer.class }) @Component @Slf4j public class TomcatCustomizerBeanPostProcessor implements BeanPostProcessor { @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof ConfigurableTomcatWebServerFactory) { ConfigurableTomcatWebServerFactory configurableTomcatWebServerFactory = (ConfigurableTomcatWebServerFactory) bean; addTomcat404CodePage(configurableTomcatWebServerFactory); } return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName); } private static void addTomcat404CodePage(ConfigurableTomcatWebServerFactory factory) { factory.addContextCustomizers((context) -> { String tomcatTempPath = context.getCatalinaBase().getPath(); log.info("tomcat目录:{}", tomcatTempPath); String path = tomcatTempPath + "/404.html"; ClassPathResource cr = new ClassPathResource("404.html"); if (cr.exists()) { File file404 = new File(path); if (!file404.exists()) { try { FileCopyUtils.copy(cr.getInputStream(), FileUtil.openOutputStream(file404)); } catch (IOException e) { e.printStackTrace(); } } } ErrorReportValve valve = new ErrorReportValve(); valve.setProperty("errorCode.404", path); valve.setShowServerInfo(false); valve.setShowReport(false); valve.setAsyncSupported(true); context.getParent().getPipeline().addValve(valve); }); } }
The above two approaches, both can be achieved, if (path of non-project path) project with the project name to access, access any wrong path, point to a custom 404 page