自定义Spring Boot内置Tomcat的404页面

spring boot 的相关404页面配置都是针对项目路径下的(如果配置了 context-path)

在context-path不为空的情况下,如果访问路径不带context-path,这时候会显示空白页面或者是tomcat默认404页面

这时候如何自定义内置tomcat的404页面呢?

查看tomcat错误页面的实现源码org.apache.catalina.valves.ErrorReportValue:

report方法中先查找是否注册了错误页面,默认情况未注册任何错误页面,然后通过sendErrorPage方法发送错误页面

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;
}

由于spring boot 默认打成的jar包运行tomcat,所以必须要把404页面放到外部,这里先将404.html放到resource目录下,然后启动过程中将页面复制到tomcat临时目录,将404路径指向该页面就可以了。

这里有两种实现办法:

1、通过AOP修改默认注册的ErrorReportValue

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、通过自定义BeanPostProcessor添加自定义的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);
});
}
}

上面两种办法,都就可以达到,如果项目访问带项目名,访问任意错误路径(非项目路径下的路径),指向自定义的404页面

郑重声明:本网站发布的内容(图片、视频和文字)以及用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服

发表评论

登录后才能评论