SpringBoot2.3定制错误页面的方法示例

 更新时间:2020年8月12日 13:03  点击:1870

一. 问题背景

后台: SpringBoot 2.3.1(官方2.3版本修改了很多,抛弃了很多以前能用的方法)
前端: Layui(前端用哪个框架问题不大)

技术: SpringBoot+Thymeleaf+Layui

情况:我想将Layui提供好的错误页面作为SpringBoot默认的错误页面,而且Layui提供的错误页面位置并不是放在/静态资源文件夹/error,而是在如下:

二. SpringBoot的错误页面机制

错误页面机制的原理详情可以看Day41——错误处理原理&定制错误页面以及Day42——定制错误数据。

首先要知道SpringBoot的错误页面机制原理自动配置是由ErrorMvcAutoConfiguration配置的。所以定制错误页面的解决方案都可以参考ErrorMvcAutoConfiguration类以及参考他人博客。

这里只做简单的回顾,如下:

三. 定制错误页面

首先我项目里面在application.properties配置了静态资源路径为classpath:/templates/layuimini/,如下:

#自定义静态资源路径
spring.resources.static-locations=classpath:/templates/layuimini/

大家根据需要自行调整自己项目里面的静态资源路径,后面的定制错误页面的路径会根据这个配置好的路径去寻找,或者拼串。

3.1 方案一(最简单的,但是不推荐)

3.1.1 步骤

最简单是 在静态资源文件夹下面创建一个error文件夹,在里面放置自己的错误页面,如下:


不推荐的原因是,我当前的目录结构是所有的页面都是放在/templates/layuimini/page/文件夹下面的,如果按照上面做法,会破坏我的目录结构,后期维护很困难。

3.1.2 原理

在BasicErrorController中,封装视图的时候,当前项目如果有模板引擎,会先用模板引擎解析,找不到再去静态资源文件夹寻找视图(视图名是error/状态码.html,这是指error文件夹下的状态码.html文件,这是由私有方法实现的,所以外部无法修改)。因此上面的步骤就是SpringBoot先去templates文件夹下找,找不到,再去/templates/layuimini/这个静态资源文件夹找(前提是你设置了静态资源文件夹;否则默认按SpringBoot的默认静态资源文件夹找,比如resources、public、static、resource)

3.2 方案二(不能实现自适应定制错误页面,不推荐)

3.2.1 步骤

首先放置好自定义的错误页面,是在/templates/layuimini/page/error/,如下:


然后再创建一个MyErrorPageConfig类,最最最关键的是new ErrorPage()中第二个入参,如果是加了.html,那么就会直接找页面,如果没有加,那么将它当作请求去找controller,如下:

@Configuration
public class MyErrorPageConfig {

  @Bean
  public WebServerFactoryCustomizer<ConfigurableWebServerFactory> webServerFactoryCustomizer(){
    return new WebServerFactoryCustomizer<ConfigurableWebServerFactory>() {
      @Override
      public void customize(ConfigurableWebServerFactory factory) {
        ErrorPage errorPage1 = new ErrorPage(HttpStatus.NOT_FOUND, "/page/error/4xx.html");
        ErrorPage errorPage2 = new ErrorPage(HttpStatus.NOT_FOUND, "/page/error/5xx.html");
        factory.addErrorPages(errorPage1, errorPage2);
      }
    };
  }
}

3.2.2 原理

详情可以参考Day46——SpringBoot2.x版本的嵌入式Servlet容器自动配置原理以及Day47——嵌入式Servlet容器启动原理

首先要知道SpringBoot2.x版本的嵌入式Servlet容器是由ServletWebServerFactoryAutoConfiguration类配置的。一切的配置信息以及解决方案都可以参考这个类。

这里只做简单解释,如下:


这样它就会执行上面步骤中的customize()方法中的方法

3.3 方案三(实现自适应)

自适应就是根据发送的/error请求是浏览器还是客户端,使用不同的controller方法进行处理,并返回不同类型的数据

3.3.1 步骤

创建一个实现了ErrorController接口的MyBasicErrorController类,如下:

/**
 * 定制ErrorController,目的是能使SpringBoot找到自己定制的错误页面
 * 大部分的代码BasicController一致,关键点是修改错误页面的路径
 */
@Controller
@RequestMapping(value = "/error")
public class MyBasicErrorController implements ErrorController {


  @RequestMapping(produces = {"text/html"})//返回给浏览器
  public String handlerError(HttpServletRequest request, Model model){
    WebRequest webRequest = new ServletWebRequest(request);//对request进行包装,目的是能操作更多的方法
    HttpStatus status = this.getStatus(request);//获取status

    String path = (String) webRequest.getAttribute("javax.servlet.error.request_uri", 0);
    String message = (String) webRequest.getAttribute("javax.servlet.error.message", 0);
    if(message.equals("")){
      message = "No Available Message";
    }

    //携带错误数据信息
    model.addAttribute("timestamp", new Date());
    model.addAttribute("statusCode", status.value());
    model.addAttribute("error", status.getReasonPhrase());
    model.addAttribute("message", message);
    model.addAttribute("path", path);

    int i = status.value() / 100;//判断是4xx还是5xx错误
    if(i == 4){
      return "layuimini/page/error/4xx";//使用自己定制的错误页面
    }else if(i == 5){
      return "layuimini/page/error/5xx";//使用自己定制的错误页面
    }
    return null;
  }

  @RequestMapping//返回给客户端
  public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
    WebRequest webRequest = new ServletWebRequest(request);//对request进行包装,目的是能操作更多的方法
    HttpStatus status = this.getStatus(request);//获取status
    Map<String, Object> map = new HashMap<>();

    if (status == HttpStatus.NO_CONTENT) {
      return new ResponseEntity(status);
    } else {

      String path = (String) webRequest.getAttribute("javax.servlet.error.request_uri", 0);
      String message = (String) webRequest.getAttribute("javax.servlet.error.message", 0);

      map.put("timestamp", new Date());
      map.put("statusCode", status.value());
      map.put("error", status.getReasonPhrase());
      map.put("message", message);
      map.put("path", path);

      return new ResponseEntity(map, status);
    }
  }



  protected HttpStatus getStatus(HttpServletRequest request) {
    Integer statusCode = (Integer)request.getAttribute("javax.servlet.error.status_code");
    if (statusCode == null) {
      return HttpStatus.INTERNAL_SERVER_ERROR;
    } else {
      try {
        return HttpStatus.valueOf(statusCode);
      } catch (Exception var4) {
        return HttpStatus.INTERNAL_SERVER_ERROR;
      }
    }
  }

  @Override
  public String getErrorPath() {
    return "null";
  }
}

3.3.2 原理

SpringBoot的错误页面机制的自适应,是由BasicErrorController实现的,而这个BasicErrorController只有在容器中没有ErrorController的情况下,才会被注册进容器,因此我们创建一个实现了ErrorController接口的类,这个BasicErrorController就失效,然后我们仿照BasicErrorController里面的方法来实现自己的controller就可以了。如下:

@Bean
  @ConditionalOnMissingBean(
    value = {ErrorController.class},//没有ErrorController才会去注册BasicErrorController
    search = SearchStrategy.CURRENT
  )
  public BasicErrorController basicErrorController(ErrorAttributes errorAttributes, ObjectProvider<ErrorViewResolver> errorViewResolvers) {
    return new BasicErrorController(errorAttributes, this.serverProperties.getError(), (List)errorViewResolvers.orderedStream().collect(Collectors.toList()));
  }

上面实现步骤中的一些错误数据是参照DefaultErrorAttributes中的方法实现的

到此这篇关于SpringBoot2.3定制错误页面的方法示例的文章就介绍到这了,更多相关SpringBoot2.3定制错误页面内容请搜索猪先飞以前的文章或继续浏览下面的相关文章希望大家以后多多支持猪先飞!

[!--infotagslink--]

相关文章

  • 解决springboot使用logback日志出现LOG_PATH_IS_UNDEFINED文件夹的问题

    这篇文章主要介绍了解决springboot使用logback日志出现LOG_PATH_IS_UNDEFINED文件夹的问题,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-04-28
  • SpringBoot实现excel文件生成和下载

    这篇文章主要为大家详细介绍了SpringBoot实现excel文件生成和下载,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2021-02-09
  • 详解springBoot启动时找不到或无法加载主类解决办法

    这篇文章主要介绍了详解springBoot启动时找不到或无法加载主类解决办法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-09-16
  • SpringBoot集成Redis实现消息队列的方法

    这篇文章主要介绍了SpringBoot集成Redis实现消息队列的方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-02-10
  • 解决Springboot get请求是参数过长的情况

    这篇文章主要介绍了解决Springboot get请求是参数过长的情况,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-09-17
  • Spring Boot项目@RestController使用重定向redirect方式

    这篇文章主要介绍了Spring Boot项目@RestController使用重定向redirect方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-09-02
  • Springboot+TCP监听服务器搭建过程图解

    这篇文章主要介绍了Springboot+TCP监听服务器搭建过程,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2020-10-28
  • springBoot 项目排除数据库启动方式

    这篇文章主要介绍了springBoot 项目排除数据库启动方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-09-10
  • springboot中使用@Transactional注解事物不生效的坑

    这篇文章主要介绍了springboot中使用@Transactional注解事物不生效的原因,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-01-26
  • SpringBoot接口接收json参数解析

    这篇文章主要介绍了SpringBoot接口接收json参数解析,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-10-19
  • 详解SpringBoot之访问静态资源(webapp...)

    这篇文章主要介绍了详解SpringBoot之访问静态资源(webapp...),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-09-14
  • springboot多模块包扫描问题的解决方法

    这篇文章主要介绍了springboot多模块包扫描问题的解决方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-09-16
  • Springboot mybatis plus druid多数据源解决方案 dynamic-datasource的使用详解

    这篇文章主要介绍了Springboot mybatis plus druid多数据源解决方案 dynamic-datasource的使用,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2020-11-18
  • Springboot实现多线程注入bean的工具类操作

    这篇文章主要介绍了Springboot实现多线程注入bean的工具类操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-08-27
  • Springboot+MDC+traceId日志中打印唯一traceId

    本文主要介绍了Springboot+MDC+traceId日志中打印唯一traceId,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2021-10-17
  • SpringBoot部署到Linux读取resources下的文件及遇到的坑

    本文主要给大家介绍SpringBoot部署到Linux读取resources下的文件,在平时业务开发过程中,很多朋友在获取到文件内容乱码或者文件读取不到的问题,今天给大家分享小编遇到的坑及处理方案,感兴趣的朋友跟随小编一起看看吧...2021-06-21
  • 关于springboot中nacos动态路由的配置

    这篇文章主要介绍了springboot中nacos动态路由的配置方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-09-11
  • SpringBoot高版本修改为低版本时测试类报错的解决方案

    这篇文章主要介绍了SpringBoot高版本修改为低版本时测试类报错的解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-09-18
  • 解决Springboot整合shiro时静态资源被拦截的问题

    这篇文章主要介绍了解决Springboot整合shiro时静态资源被拦截的问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-01-26
  • springboot配置多数据源后mybatis拦截器失效的解决

    这篇文章主要介绍了springboot配置多数据源后mybatis拦截器失效的解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-09-23