自定義內置 tomcat,自定義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路徑指向該頁面就能夠了。html

這裏有兩種實現辦法:java

一、經過AOP修改默認註冊的ErrorReportValueweb

 

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

二、經過自定義BeanPostProcessor添加自定義的ErrorReportValvespring

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頁面apache

相關文章
相關標籤/搜索