Custom built tomcat Spring Boot 404 pages

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

Guess you like

Origin www.cnblogs.com/rinack/p/11239495.html