• 自定义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页面

  • 相关阅读:
    RocketMQ读书笔记3——消费者
    RocketMQ读书笔记1——简述
    02_dubbo实例_多版本号
    01_dubbo实例_服务分组
    分布式开放消息系统(RocketMQ)的原理与实践
    关于ajax的那些事
    关于html5之canvas的那些事
    关于js封装框架类库之属性操作
    关于js封装框架类库之样式操作
    关于js封装框架类库之事件模块
  • 原文地址:https://www.cnblogs.com/rinack/p/11239495.html
Copyright © 2020-2023  润新知