SpringBoot请求错误如404可能看到如下页面:

有时可能需要自定义错误页面针对不同的http.status,如404/400。

【1】解决方法

① 注册错误页面

如下所示:

@Componentpublic class ErrorPageConfig implements ErrorPageRegistrar {    @Override    public void registerErrorPages(ErrorPageRegistry registry) {        ErrorPage error400Page = new ErrorPage(HttpStatus.BAD_REQUEST, "/error/404");        ErrorPage error404Page = new ErrorPage(HttpStatus.NOT_FOUND, "/error/404");        ErrorPage error500Page = new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/error/500");        registry.addErrorPages(error400Page,error404Page,error500Page);    }}

② controller进行拦截

然后你只需要写个controller拦截不同请求然后跳到不同的自定义错误页面即可,如下所示:

@RequestMapping("/error/{status}")public String errorPage(@PathVariable Integer status){    switch (status){        case 401:        case 400:return "/error/404";        case 500:return "/error/500";        default:return "/error/default";    }}

那么原理呢?

【2】原理讲解

① 启动SpringBoot,注册错误页面

如下图所示,启动项目时候再onRefresh方法中会创建一个WebServer,继而获取ServletWebServerFactory。

1.1AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization

在创建bean-tomcatServletWebServerFactory时会调用AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization,如下所示:

@Overridepublic Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) throws BeansException {Object result = existingBean;for (BeanPostProcessor processor : getBeanPostProcessors()) { Object current = processor.postProcessBeforeInitialization(result, beanName); if (current == null) {  return result; } result = current;}return result;}

该方法会获取bean后置处理器,然后循环遍历调用每个bean后置处理器的postProcessBeforeInitialization方法。

1.2 ErrorPageRegistrarBeanPostProcessor.postProcessBeforeInitialization

当遍历到ErrorPageRegistrarBeanPostProcessor时会调用其postProcessBeforeInitialization方法,方法源码如下所示:

@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof ErrorPageRegistry) {  postProcessBeforeInitialization((ErrorPageRegistry) bean); } return bean;}

方法会判断当前bean是否ErrorPageRegistry类型,如果是,则调用postProcessBeforeInitialization方法,源码如下所示:

private void postProcessBeforeInitialization(ErrorPageRegistry registry) { for (ErrorPageRegistrar registrar : getRegistrars()) {  registrar.registerErrorPages(registry); }}

该方法会获取Registrars,然后循环遍历调用每一个注册器的registerErrorPages方法。获取注册其源码如下所示:

private Collection getRegistrars() { if (this.registrars == null) {  // Look up does not include the parent context  this.registrars = new ArrayList<>(    this.beanFactory.getBeansOfType(ErrorPageRegistrar.class, false, false).values());  this.registrars.sort(AnnotationAwareOrderComparator.INSTANCE);  this.registrars = Collections.unmodifiableList(this.registrars); } return this.registrars;}

故而,当我们的ErrorPageConfig 实现了ErrorPageRegistrar时,会被检测到并执行registerErrorPages方法。

② 把错误页面放到StandardContext.errorPageSupport中

StandardContext是什么?我们可以看下如下类继承示意图。

在①中我们提到会注册错误页面registrar.registerErrorPages(registry);,如下图所示此时的registry为TomcatServletWebServerFactory:

我们再来看下TomcatServletWebServerFactory继承示意图(可以看到其父类AbstractConfigurableWebServerFactory实现了ErrorPageRegistry接口):

2.1 AbstractConfigurableWebServerFactory.addErrorPages

方法源码如下:

@Overridepublic void addErrorPages(ErrorPage... errorPages) { Assert.notNull(errorPages, "ErrorPages must not be null"); this.errorPages.addAll(Arrays.asList(errorPages));}

也就说错误页面现在被放到了属性private Set errorPages = new LinkedHashSet<>();中。

2.2 TomcatServletWebServerFactory.configureContext

创建完TomcatServletWebServerFactory后会调用configureContext方法,如下图所示:

在configureContext方法中会获取错误页面然后逐个调用StandardContext.addErrorPage方法添加到其ErrorPageSupport errorPageSupport中。

configureContext方法中遍历错误页面如下所示:

for (ErrorPage errorPage : getErrorPages()) { org.apache.tomcat.util.descriptor.web.ErrorPage tomcatErrorPage = new org.apache.tomcat.util.descriptor.web.ErrorPage(); tomcatErrorPage.setLocation(errorPage.getPath()); tomcatErrorPage.setErrorCode(errorPage.getStatusCode()); tomcatErrorPage.setExceptionType(errorPage.getExceptionName()); context.addErrorPage(tomcatErrorPage);}

StandardContext.addErrorPage方法源码如下所示:

@Override public void addErrorPage(ErrorPage errorPage) {     // Validate the input parameters     if (errorPage == null)         throw new IllegalArgumentException             (sm.getString("standardContext.errorPage.required"));     String location = errorPage.getLocation();     if ((location != null) && !location.startsWith("/")) {         if (isServlet22()) {             if(log.isDebugEnabled())                 log.debug(sm.getString("standardContext.errorPage.warning",                              location));             errorPage.setLocation("/" + location);         } else {             throw new IllegalArgumentException                 (sm.getString("standardContext.errorPage.error",                               location));         }     }//调用errorPageSupport.add     errorPageSupport.add(errorPage);     fireContainerEvent("addErrorPage", errorPage); }

ErrorPageSupport.add方法如下所示:

public void add(ErrorPage errorPage) {     String exceptionType = errorPage.getExceptionType();     if (exceptionType == null) {         statusPages.put(Integer.valueOf(errorPage.getErrorCode()), errorPage);     } else {         exceptionPages.put(exceptionType, errorPage);     } }

通过该方法可以看到,不止可以通过HTTP状态码定义错误页面,还可以通过异常类型进行定义。

那么ErrorPageSupport、statusPages、exceptionPages分别是什么呢?我们看下图示意:

③ 错误页面如何被用到

在ResourceHttpRequestHandler.handleRequest方法处理请求时,找不到资源会调用response.sendError方法:

这里只需要关注这一点,无需关注细节,我们继续往下走。。。。一直走到StandardHostValve.status方法。

StandardHostValve.status中会对响应状态码进行处理。

private void status(Request request, Response response) {     int statusCode = response.getStatus();     // Handle a custom error page for this status code     Context context = request.getContext();     if (context == null) {         return;     }     /* Only look for error pages when isError() is set.      * isError() is set when response.sendError() is invoked. This      * allows custom error pages without relying on default from      * web.xml.      */     if (!response.isError()) {         return;     }//这里会从errorPageSupport.find(errorCode)获取到错误页//根据错误码,比如404从statusPages获取对应的ErrorPage对象     ErrorPage errorPage = context.findErrorPage(statusCode);     if (errorPage == null) {         // Look for a default error page         errorPage = context.findErrorPage(0);     }     if (errorPage != null && response.isErrorReportRequired()) {         response.setAppCommitted(false);         request.setAttribute(RequestDispatcher.ERROR_STATUS_CODE,                           Integer.valueOf(statusCode));         String message = response.getMessage();         if (message == null) {             message = "";         }         request.setAttribute(RequestDispatcher.ERROR_MESSAGE, message);         request.setAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR,                 errorPage.getLocation());         request.setAttribute(Globals.DISPATCHER_TYPE_ATTR,                 DispatcherType.ERROR);         Wrapper wrapper = request.getWrapper();         if (wrapper != null) {             request.setAttribute(RequestDispatcher.ERROR_SERVLET_NAME,                               wrapper.getName());         }         request.setAttribute(RequestDispatcher.ERROR_REQUEST_URI,                              request.getRequestURI());                              //这里很重要,将会尝试跳转到我们自定义错误请求页面         if (custom(request, response, errorPage)) {             response.setErrorReported();             try {                 response.finishResponse();             } catch (ClientAbortException e) {                 // Ignore             } catch (IOException e) {                 container.getLogger().warn("Exception Processing " + errorPage, e);             }         }     } }

如下图所示,在StandardHostValve.custom方法中将会调用ApplicationDispatcher.forwar进行请求转发。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

本文链接:

https://blog.csdn.net/j080624/article/details/109197726

apache 设置404 页面_SpringBoot自定义错误页面相关推荐

  1. java错误页面_java自定义错误页面实现方法

    java后台自定义错误页面:(推荐:java视频教程) java后台项目中,经常会出现404或500等错误, 如果不做设置,服务器会默认返回404或500的错误页面 给前端显示错误页面. 掌握了错误页 ...

  2. java tomcat 404配置_在Tomcat中配置404自定义错误页面详解

    一直使用tomcat,今天想到一个问题,自定义404错误页面, 为了获得很好的用户感受,是不应当向用户暴露404这样的页面的, 问题的出发点是我在Struts2中定义错误页面, 在Struts2中是这 ...

  3. JSP JAVA 自定义 错误页面(404,505,500)

    当网站页面找不到或者服务器内部出错的时候,我们不想让用户看到默认的那张 404,500 的错误页面,那要是想自己做一张 找不到页面的页面改怎么做呢? 在 web .xml 文件中 加入下面的语句就能达 ...

  4. ASP.NET自定义错误页面(转)

    ASP.NET自定义错误页面(转) ASP.NET 提供三种用于在出现错误时捕获和响应错误的主要方法:Page_Error 事件.Application_Error 事件以及应用程序配置文件 (Web ...

  5. react 错误边界_React with GraphQL和错误边界中的自定义错误页面

    react 错误边界 by Abi Noda 通过Abi Noda React with GraphQL和错误边界中的自定义错误页面 (Custom error pages in React with ...

  6. SpringBoot自定义错误页面

    Spring Boot以一种新的微服务的方式来替代以Spring Framework构建项目的传统方式,我已经计划在后续的项目开发中使用它.它已经帮我们做了90%的工作,剩下10%的工作需要我们自己去 ...

  7. 017_SpringBoot异常处理方式-自定义错误页面

    1. 使用maven构建SpringBoot的名叫spring-boot-exception1项目 2. pom.xml <project xmlns="http://maven.ap ...

  8. ASP.NET Core中显示自定义错误页面-增强版

    之前的博文 ASP.NET Core中显示自定义错误页面 中的方法是在项目中硬编码实现的,当有多个项目时,就会造成不同项目之间的重复代码,不可取. 在这篇博文中改用middleware实现,并且放在独 ...

  9. SpringBoot异常处理-自定义错误页面

    自定义错误页面 SpringBoot 默认的处理异常的机制:SpringBoot 默认的已经提供了一套处理异常的机制. 一旦程序中出现了异常SpringBoot 会像/error 的url 发送请求. ...

最新文章

  1. mysql主主复制、主从复制、半同步的实现
  2. java 静态内部类 弱引用_Java基础 强引用、弱引用、软引用、虚引用
  3. Yum database disk image is malformed 错误
  4. 记-python socket服务器端四部曲
  5. 外围功能电路控制 LET′S TRY“嵌入式编程”: 4 of 6
  6. 配置electron
  7. 使用XLinq.XElement读取带Namespace(命名空间)的XML
  8. 【物联网】12.物联网服务器发送方式(HTTP,WebSocket ,MQTT )
  9. 【评测】Alfa Aesar分子砌块
  10. 2019 acm-icpc 西安全国邀请赛 J
  11. PPT基础(三十)图片的特殊效果
  12. win10怎么把屏幕分成多个屏幕 win10把屏幕分成多个屏幕方法
  13. 虚拟机VS容器 安全比拼谁更胜一筹?
  14. Python自动获取邮箱验证码【上集】
  15. Pico XR Unity开发时如何设置应用版权保护
  16. leetCode第199场周赛学习
  17. mmc0: error -84 whilst initialising SD card
  18. C++编程第一步:输出100以内的奇数和
  19. ACM传奇之路(紧握着自己颤抖的双手)
  20. IP地址和子网划分学习笔记之《IP地址详解》

热门文章

  1. 重新绑定ItemsSource先设置ItemsSource = null;的原因
  2. 递归实现 十进制转换其他进制(2-16)
  3. 对于大规模机器学习的理解和认识
  4. [Postgres]Postgres复制表
  5. PeerJS 0.1.7:一个用于浏览器内P2P的WebRTC封装器
  6. javascript实例_网页空降与抖动
  7. DOM-10 面向对象开发Todolist
  8. 2.7万字还原行业面貌,《2019 AI金融风控行业研究报告》正式上线!...
  9. Linux命令行编辑快捷键
  10. 生成GUID唯一值的方法汇总(dotnet/javascript/sqlserver)