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页面

自定义Spring Boot内置tomcat的404页面的更多相关文章

  1. Spring boot 内置tomcat禁止不安全HTTP方法

    Spring boot 内置tomcat禁止不安全HTTP方法 在tomcat的web.xml中可以配置如下内容,让tomcat禁止不安全的HTTP方法 <security-constraint ...

  2. Spring boot内置Tomcat的临时目录被删除导致文件上传不了-问题解析

    目录 1.问题 2.1. 为什么需要使用这个/tmp/tomcat*? 2.2.那个 /tmp/tomcat* 目录为什么不存在? 三.解决办法 修改 springboot 配置,不要在/tmp 下创 ...

  3. Spring Boot内置Tomcat

    Spring Boot默认支持Tomcat/Jetty/Undertow作为底层容器.在之前实战相关的文章中,可以看到引入spring-boot-starter-web就默认使用tomcat容器,这是 ...

  4. 配置spring boot 内置tomcat的accessLog日志

    #配置内置tomcat的访问日志server.tomcat.accesslog.buffered=trueserver.tomcat.accesslog.directory=/home/hygw/lo ...

  5. 如何优雅的关闭基于Spring Boot 内嵌 Tomcat 的 Web 应用

    背景 最近在搞云化项目的启动脚本,觉得以往kill方式关闭服务项目太粗暴了,这种kill关闭应用的方式会让当前应用将所有处理中的请求丢弃,响应失败.这种形式的响应失败在处理重要业务逻辑中是要极力避免的 ...

  6. 009-Spring Boot 事件监听、监听器配置与方式、spring、Spring boot内置事件

    一.概念 1.事件监听的流程 步骤一.自定义事件,一般是继承ApplicationEvent抽象类 步骤二.定义事件监听器,一般是实现ApplicationListener接口 步骤三.启动时,需要将 ...

  7. Spring Boot 内嵌Tomcat的端口号的修改

    操作非常的简单,不过如果从来没有操作过,也是需要查找一下资料的,所以,在此我简单的记录一下自己的操作步骤以备后用! 1:我的Eclipse版本,不同的开发工具可能有所差异,不过大同小异 2:如何进入对 ...

  8. Spring Boot内嵌Tomcat session超时问题

    最近让Spring Boot内嵌Tomcat的session超时问题给坑了一把. 在应用中需要设置session超时时间,然后就习惯的在application.properties配置文件中设置如下, ...

  9. Spring Boot启动过程(四):Spring Boot内嵌Tomcat启动

    之前在Spring Boot启动过程(二)提到过createEmbeddedServletContainer创建了内嵌的Servlet容器,我用的是默认的Tomcat. private void cr ...

随机推荐

  1. vue-cli 移动端项目如何在手机上调试预览

    这里分享下如何在webpack工具构建下的vue项目,在手机端调试和预览,言归正传. 1.电脑和手机连接到同一个WIFI a.台式电脑和手机同时链接一个路由器,使用同一个wifi: b.笔记本也可以直 ...

  2. JWT生成token及过期处理方案

    业务场景 在前后分离场景下,越来越多的项目使用token作为接口的安全机制,APP端或者WEB端(使用VUE.REACTJS等构建)使用token与后端接口交互,以达到安全的目的.本文结合stacko ...

  3. Windows系统Apache下载和安装

    一.Apache的下载 1.访问Apache官网:https://httpd.apache.org 2.选择Windows版本下载 3.下载完成后解压缩,把文件放到自己想放的盘 二.Apache的安装 ...

  4. Linux chown命令详解使用格式和方法

    指令名称 : chown 使用权限 : root(一般来说,这个指令只有是由系统管理者(root)所使用,一般使用者没有权限可以改变别人的文件拥有者,也没有权限可以自己的文件拥有者改设为别人.只有系统 ...

  5. Class.getDeclaredFields()和Class.getFields()的区别。 Class.getMethods()和Class.getDeclaredMethods()的区别。

    package www.cn.extend; /** Animal * 2019/07/04 * @author Administrator * */ public class Animal { pu ...

  6. CENTOS6.5源码安装LNMP

    CENTOS6.5源码安装LNMP 一.安装前准备 ########################################################################## ...

  7. 【RocketMQ异常】Caused by: com.aliyun.openservices.shade.com.alibaba.rocketmq.client.exception.MQClientException: No route info of this topic, message-service-topic-testf

    一.异常信息 -- ::-thread-] ERROR c.x.x.r.service.producer.ali.AliMQProducerProcess.sendMessageFromQueue(A ...

  8. Python,for循环小例子--99乘法表

    一.99乘法表 for i in range(1, 10): for j in range(1, i + 1): print('%sx%s=%s ' % (j, i, j * i), end='') ...

  9. c++查询特定字符串位置

    size_t find (const string& str, size_t pos = 0) const noexcept;(摘自c++官网:std::string::find) size_ ...

  10. [转]Linux-虚拟网络设备-tun/tap

    转: 原文:https://blog.csdn.net/sld880311/article/details/77854651 ------------------------------------- ...