SpringBoot 错误和异常捕获

全局异常处理:

@ControllerAdvice 最常见的使用场景就是全局异常处理。比如上传文件超过了限制大小,就会抛出异常,此时可以通过@ControllerAdvice结合@ExceptionHandler定义全局异常捕获机制。

当我们没有定义异常捕获机制的时候,控制台会报错:

 

 

 

在src中添加exception文件夹,然后添加自定义异常处理类:

 

 

 CustomExceptionHandler.java

package com.example.demo.exception;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.http.HttpServletResponse;

import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.multipart.MaxUploadSizeExceededException;
@ControllerAdvice
public class CustomExceptionHandler {

    @ExceptionHandler(MaxUploadSizeExceededException.class)
    public void uploadException(MaxUploadSizeExceededException e, HttpServletResponse resp) throws IOException{
        resp.setContentType("text/html;charset=UTF-8");
        PrintWriter outPrintWriter = resp.getWriter();
        outPrintWriter.write("上传文件大小超过限制!");
        outPrintWriter.flush();
        outPrintWriter.close();
        
        System.out.print("上传文件大小超过限制!");
    }
}

注意要给类添加@ControllerAdvice,当系统启动时,该类就会被扫描到Spring容器中,然后定义uploadException方法,该方法上添加了@ExceptionHandler注解,其中定义的MaxUploadSizeExceededException.class表明该方法用来处理MaxUploadSizeExceededException类型的异常。如果想让该方法处理所有类型的异常,只需将MaxUploadSizeExceededException 改为 Exception即可。

 

 

 调试查看,发现console中不会再输出大段错误信息,而是被我们写的ExceptionHandler类捕获之后,显示出了我们定制的文字。

 

 

自定义错误页

目的与原理:

  处理异常时,若我们想根据实际情况返回不同的页面,@ControllerAdvice与@ExceptionHandler,一般用于处理应用级别的异常,一些容器级别的错误就处理不了,例如Filter中抛出异常,SpringBoot对于错误会有一个默认页面给用户显示出来,现在我们想显示自己的页面。

  SpringBoot在返回错误信息时不一定返回HTML页面,而是根据实际情况返回HTML或者一段JSON,我们可以将这二者自己定制。

  SpringBoot中的错误默认是由BasicErrorController类来处理的,这个类核心方法有两个:

@Controller
public class MyErrorController extends BasicErrorController {
    @Autowired
    public MyErrorController(ErrorAttributes errorAttributes,
                             ServerProperties serverProperties,
                             List<ErrorViewResolver> errorViewResolvers) {
        super(errorAttributes, serverProperties.getError(), errorViewResolvers);
    }
    @Override
    public ModelAndView errorHtml(HttpServletRequest request,
                                  HttpServletResponse response) {
        HttpStatus status = getStatus(request);
        Map<String, Object> model = getErrorAttributes(
                request, isIncludeStackTrace(request, MediaType.TEXT_HTML));
        model.put("custommsg", "出错啦!");
        ModelAndView modelAndView = new ModelAndView("myErrorPage", model, status);
        return modelAndView;
    }
    @Override
    public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
        Map<String, Object> body = getErrorAttributes(request,
                isIncludeStackTrace(request, MediaType.ALL));
        body.put("custommsg", "出错啦!");
        HttpStatus status = getStatus(request);
        return new ResponseEntity<>(body, status);
    }
}
errorHtml方法用来返回错误HTML页面,error用来返回错误json,这两个究竟返回哪个,看请求头Accept参数。首先看返回HTML页面的errorHTML方法,通过调用resolveErrorView方法来获取一个ModelAndView。而resloveErrorView方法的调用最终会来到DefaultErrorViewResolver类中,这个类是SpringBoot中默认的错误信息视图解析器。

 

 

 

 

 

   从DefaultErrorViewResolver类的部分源码中看到,SpringBoot默认是在error目录下查找4xx,5xx的文件作为错误视图,当找不到会回到errorHtml方法中,然后使用error作为默认的错误页面视图名,如果没有名字是error的页面,就会以默认的错误页面显示。

 

1.简单配置:

controller:

  

 

 

 

  如果我们只是简单使用,直接在/static/error/下创建4xx.html,5xx.html的页面即可,也可以使用响应码直接命名文件若404.html,500.html。

 

 

 这样只能使用静态HTML页面,无法向用户展示完整的错误信息,使用我们要用模板语言,比如用Thymeleaf模板语言为例,Thymeleaf默认处于classpath:/templates/目录下,如图:

 

 

4xx.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org/">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
<table border="1">
  <tr>
    <td>timestamp</td>
    <td th:text="${timestamp}"></td>
  </tr>
  <tr>
    <td>status</td>
    <td th:text="${status}"></td>
  </tr>
  <tr>
    <td>error</td>
    <td th:text="${error}"></td>
  </tr>
  <tr>
    <td>message</td>
    <td th:text="${message}"></td>
  </tr>
  <tr>
    <td>path</td>
    <td th:text="${path}"></td>
  </tr>
</table>
</body>
</html>

此时,有一个优先级问题,如以上,响应码.html文件优先于4xx.html文件动态页面高于静态页面(/templates/与/static/下的同名文件,/templates/下先展示)。

 

2.复杂配置:

  上面的针对HTML页面无法处理JSON定制问题,SpringBoot中支持对Error信息的深度定制:

2.1自定义Error数据

  就是对返回的数据进行自定义,SpringBoot返回的Error信息一共5条,分别是timestamp,status,error,message,path。在BasicErrorController的errorHtml和error方法中都是根据getErrorAttributes方法获取Error信息的。

  该方法最终会调用DefaultErrorAttributes类的getErrorAttributes方法,而DefaultErrorAttribute类是在ErrorMvcAutoConfiguration中默认提供的。

    我们只需要自己提供一个ErrorAttributes即可,DefaultErrorAttributes是ErrorAttributes类的子类,使用我们只需要自己写一个类继承DefaultErrorAttributes即可。

 

 

 

 

 

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org/">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<table border="1">

    <tr>
        <td>custommsg</td>
        <td th:text="${custommsg}"></td>                    //显示自定义数据
    </tr>


    <tr>
        <td>timestamp</td>
        <td th:text="${timestamp}"></td>
    </tr>
    <tr>
        <td>status</td>
        <td th:text="${status}"></td>
    </tr>
    <tr>
        <td>error</td>
        <td th:text="${error}"></td>
    </tr>
    <tr>
        <td>message</td>
        <td th:text="${message}"></td>
    </tr>
    <tr>
        <td>path</td>
        <td th:text="${path}"></td>
    </tr>
</table>
</body>
</html>

2.2自定义error视图

  自己提供一个类继承ErrorViewResolver即可

 

 

 2.3完全自定义

  前两种都是对BasicErrorController类中的某个环节进行修补,我们可以自己提供一个类实现ErrorController接口。

 

 

 

 

posted on 2020-03-17 12:09  atomgame的记事本  阅读(879)  评论(0编辑  收藏  举报

导航