SpringBoot异常处理

我们在实际开发中,会因为各种问题而导致无法正常访问网址,网站的对象是群众,如果出现各种的报错信息,对于用户的体验是非常的不好的,所以我们需要对项目的内部进行异常处理,

保证用户的体验舒爽

目录

1、异常处理一:默认异常处理机制

1)导入前端模板坐标

2)创建templates.error文件夹

3)自定义错误视图文件

4)配置模板

5)controller层模拟错误

2、异常处理二:自定义全局异常处理

1)添加异常页面error

2)自定义全局异常处理类

3、数据传输协议实现

1)定义统一返回格式

2)定义标记码类

4、异常处理三:状态码处理异常

1)统一处理程序抛出异常

2)统一处理业务异常

3)统一处理系统异常

5、异常处理四:状态码统一异常、返回处理

1)接口数据统一返回类状态码

2)controller层异常拦截处理

3)controller层返回信息拦截


完整目录结构

 

1、异常处理一:默认异常处理机制

如果访问到了不存在的页面,或服务器出现异常springboot会返回默认的错误页面

可以将自定义的错误页面放在springboot默认规则下的路径

默认规则:

  • 默认情况下boot提供了/error处理所有错误的映射

    也就是在/error目录下放置的页面可以处理对应的错误映射,4xx.html处理4xx错误

  • 浏览器客户端会响应一个 “whitelabel” 错误视图

资源文件夹目录

 

1)导入前端模板坐标

 <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-thymeleaf</artifactId>
 </dependency>
 <dependency>
     <groupId>com.alibaba</groupId>
     <artifactId>fastjson</artifactId>
     <version>2.0.25</version>
 </dependency>

2)创建templates.error文件夹

resource资源文件夹下,目录结构

 

3)自定义错误视图文件

把异常页面统一放在templates的error文件夹下

404、500

4)配置模板

加上这段配置

 
spring.thymeleaf.prefix=classpath:/templates/

5)controller层模拟错误

 //请求异常
     @RequestMapping("/index01")
     public String testError(){
         //这里报错
         int i=10/0;
         return "redirect:/login.html";
     }
     //请求正确
     @RequestMapping("/index02")
     public String testError02(){
         return "redirect:/login.html";
     }

这段代码必须加上,不然异常无法处理,会出现找不到localhost页面,而不会报出异常

 @RequestMapping("/error")
     public String handleError(HttpServletRequest request) {
         // 发生异常后的处理逻辑
         return "error.html";
     }

这样无论出现什么形式的报错,异常信息都会被捕获,然后抛出对应的异常页面信息

比如:我输入一个不存在的路径Title

它会获取异常抛出异常页面

 

2、异常处理二:自定义全局异常处理

实际项目处理中会出现各种各样的异常,如果以上面的方式处理异常,我们就需要写大量的异常处理页面,太过麻烦,可以定义中一个全部异常处理页面

使用了全局异常处理后原本的/error页面就会失效

1)添加异常页面error

thymeleaf会自动查找名为“error”的视图来呈现任何错误

该页面路径:src/main/resources/templates

 <body>
 <div th:text="${exception}"></div>
 <div th:text="${url}"></div>
 <h1>页面状态异常!!!</h1>
 </body>

2)自定义全局异常处理类

共有三种方法:这里举例两种

一、注解

自定义一个全局异常处理类,只需上面加上 @ControllerAdvice 注解

同时要在执行异常处理的方法上标记@ExceptionHandler

@ControllerAdvice注解的原理是SpringAOP提供的,将Controller层的方法作为切面,从而对Controller层方法进行拦截处理

 @Slf4j
 @ControllerAdvice
 public class GlobalExceptionHandler02 {
     @ExceptionHandler({ArithmeticException.class})
     public String handleArithmeticException(){
         //可输出日志信息
         log.info("算术异常处理");
         return "error";
     }
     @ExceptionHandler({NullPointerException.class})
     public String handleNullPointerException(Exception e){
         log.info("空指针异常处理");
         return "error";
     }
 }

运行结果:

控制台不会出现原来的报错信息,报错异常以日志的形式打印输出

 

这样可以保证无论出现何种异常都会跳转到这个异常页面

 

不足:无法获取异常信息

二、优化

 @Slf4j
 @ControllerAdvice
 public class GlobalExceptionHandler implements ErrorController {
 ​
     @ExceptionHandler(value = Exception.class)
     public ModelAndView defaultErrorHandler(HttpServletRequest request, Exception e) throws Exception {
         String url=request.getRequestURL().toString();
         log.error("URL:"+url);
         log.error("Exception:"+e);
         ModelAndView mav = new ModelAndView("error");
         // 添加错误信息到模型中
         mav.addObject("exception", e);
         mav.addObject("url", url);
         return mav;
     }
 }

页面效果:

 

控制台效果:

 

三、HandlerExceptionResolver接口处理异常

 @Configuration
 public class HandlerExceptionResolverImpl implements HandlerExceptionResolver {
 ​
     public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
         String url=request.getRequestURL().toString();
         ModelAndView modelAndView=new ModelAndView("error");
         modelAndView.addObject("exception", ex);
         modelAndView.addObject("url", url);
         modelAndView.addObject("msg","实现HandlerExceptionResolver接口处理异常");
         return modelAndView;
     }
 }

3、数据传输协议实现


由于我们返回值的类型多样,不利于前端处理,因此需要用统一格式封装结果集,并补充一些提示信息,实现表现层和前端数据传输


1)定义统一返回格式

 @Data
 @AllArgsConstructor
 @NoArgsConstructor
 public class Result {
     //是否成功
     private Boolean flag;
     //返回码
     private Integer code;
     //返回消息
     private String message;
     //返回数据
     private Object data;
     
     public Result(Integer code,Object data){
         this.data=data;
         this.code=code;
     }
     public Result(Integer code,Object data,Boolean flag){
         this.data=data;
         this.code=code;
         this.flag=flag;
     }
 }

2)定义标记码类

 
public class StatusCode {
     //成功
     public static final int OK=20000;
     //失败
     public static final int ERROR=20001;
     //用户名或密码错误
     public static final int LOGINERROR=20002;
     //权限不足
     public static final int ACCESSERROR=20003;
     //远程调用失败
     public static final int REMOTEERROR=20004;
     //重复操作
     public static final int REPERROR=20005;
     //没有对应的抢购数据
     public static final int NOTFOUNDERROR=20006;
 ​
     /**
      * 业务操作结果反馈
      */
     public static final Integer SAVE_OK = 20011;
     public static final Integer DELETE_OK = 20021;
     public static final Integer UPDATE_OK = 20031;
     public static final Integer GET_OK = 20041;
 ​
     public static final Integer SAVE_ERR = 20010;
     public static final Integer DELETE_ERR = 20020;
     public static final Integer UPDATE_ERR = 20030;
     public static final Integer GET_ERR = 20040;
 ​
     /**
      * 异常类型标识
      */
     public static final Integer SYSTEM_ERR = 50001;
     public static final Integer BUSINESS_ERR = 50002;
     public static final Integer SYSTEM_UNKNOW_ERR = 59999;
 }

4、异常处理三:状态码处理异常

前面所写的类统统先注掉

1)统一处理程序抛出异常

 
@RestControllerAdvice
 public class ProjectExceptionAdvice {
     //其他异常或者自定义异常可以自行拦截,修改 @ExceptionHandler即可
     //异常1
     //异常2
     
     //拦截所有的异常信息——如果以上异常都拦截不住这就是最后的防线
     @ExceptionHandler(Exception.class)
     public Result doException(Exception ex){
         //记录日志
         //通知运维
         //通知开发
         ex.printStackTrace();
         return new Result(false,StatusCode.ERROR, ex.getMessage());
     }
 }    

注意:注解@ExceptionHandler不可以在一个类中同时存在

2)统一处理业务异常

 public class BusinessException extends RuntimeException{
 ​
     private Integer code;
 ​
     public Integer getCode() {
         return code;
     }
 ​
     public void setCode(Integer code) {
         this.code = code;
     }
 ​
     public BusinessException(Integer code, String message) {
         super(message);
         this.code = code;
     }
 ​
     public BusinessException(Integer code, String message, Throwable cause) {
         super(message, cause);
         this.code = code;
     }
 }

3)统一处理系统异常

 public class SystemException extends RuntimeException{
     private Integer code;
 ​
     public Integer getCode() {
         return code;
     }
 ​
     public void setCode(Integer code) {
         this.code = code;
     }
 ​
     public SystemException(Integer code, String message) {
         super(message);
         this.code = code;
     }
 ​
     public SystemException(Integer code, String message, Throwable cause) {
         super(message, cause);
         this.code = code;
     }
 }

异常处理的效果

页面返回结果

 

控制台响应结果

 

控制台响应结果和页面显示的一致

后端控制台正常报错

5、异常处理四:状态码统一异常、返回处理

异常处理三中我返回了异常信息到页面,但后端却没有数据信息的返回

并且异常处理类太多了,这样并不好

注释掉原先的类

1)接口数据统一返回类状态码

①结果集Result02

 @Data
 public class Result02<T> implements Serializable {
     //是否成功
     private Boolean flag;
     //返回码
     private Integer code;
     //返回消息
     private String message;
     //返回数据
     private T data;
 ​
     public Result02(Boolean flag, Integer code, String message, T data) {
         this.flag = flag;
         this.code = code;
         this.message = message;
         this.data = data;
     }
 ​
     public Result02(Boolean flag, Integer code, String message) {
         this.flag = flag;
         this.code = code;
         this.message = message;
     }
 ​
     public Result02() {
         this.flag=true;
         this.code=StatusCode.OK;
         this.message="操作成功!!!";
     }
 }

②返回码StatusCode

 public class StatusCode {
     //成功
     public static final int OK=20000;
     //失败
     public static final int ERROR=20001;
     //用户名或密码错误
     public static final int LOGINERROR=20002;
     //权限不足
     public static final int ACCESSERROR=20003;
     //远程调用失败
     public static final int REMOTEERROR=20004;
     //重复操作
     public static final int REPERROR=20005;
     //没有对应的抢购数据
     public static final int NOTFOUNDERROR=20006;
 ​
     /**
      * 业务操作结果反馈
      */
     public static final Integer SAVE_OK = 20011;
     public static final Integer DELETE_OK = 20021;
     public static final Integer UPDATE_OK = 20031;
     public static final Integer GET_OK = 20041;
 ​
     public static final Integer SAVE_ERR = 20010;
     public static final Integer DELETE_ERR = 20020;
     public static final Integer UPDATE_ERR = 20030;
     public static final Integer GET_ERR = 20040;
 ​
     /**
      * 异常类型标识
      */
     public static final Integer SYSTEM_ERR = 50001;
     public static final Integer BUSINESS_ERR = 50002;
     public static final Integer SYSTEM_UNKNOW_ERR = 59999;
 }

2)controller层异常拦截处理

这里的@ExceptionHandler不能在一个类中同时存在

 
@RestControllerAdvice
 public class ProjectExceptionAdvice {
     //拦截所有的异常信息
     //@ExceptionHandler(Exception.class)
     public Result doException(Exception ex){
         //记录日志
         //通知运维
         //通知开发
         ex.printStackTrace();
         return new Result(StatusCode.ERROR, ex.getMessage(), false);
     }
     //这里我再定义了一个结果类Reslut02,和上一个方法基本相同
     //拦截所有的异常信息
     @ExceptionHandler(Exception.class)
     public Result02 doException02(Exception ex){
         //记录日志
         //通知运维
         //通知开发
         ex.printStackTrace();
         return new Result02(false, StatusCode.ERROR, ex.getMessage());
     }
 }

3)controller层返回信息拦截

 
@ControllerAdvice
 public class ResponseBodyHandle implements ResponseBodyAdvice<Result02> {
     private Logger logger= (Logger) LoggerFactory.getLogger(ResponseBodyHandle.class);
 ​
     public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
         //支持所有方法
         return true;
     }
 ​
 public Result02 beforeBodyWrite(Result02 body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
         String methodName= returnType.getMethod().getName();
         logger.info("{}方法返回值={}",methodName,JSON.toJSONString(body));
         return body;
     }
 }

结果:

前端页面显示结果

 

后端结果显示

这样就实现了前端和后端的异常拦截和信息返回处理

  • 29
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
SpringBoot提供了一套默认的异常处理机制。一旦程序出现异常,SpringBoot会向/error的URL发送请求,并通过BasicErrorController来处理该请求。默认情况下,SpringBoot会跳转到默认显示异常信息的页面来展示异常信息。如果我们希望将所有的异常统一跳转到自定义的错误页面,可以在src/main/resources/templates目录下创建一个名为error.html的页面。通过覆盖默认的错误页面,我们可以实现自定义的异常处理。 除了使用SpringBoot的默认配置外,还可以通过自定义错误页面来处理异常。我们可以在src/main/resources/templates目录下创建error.html页面,并将其命名为error。通过这种方式,我们可以自定义错误页面的内容和样式来展示异常信息。 在处理异常的过程中,可以关注ErrorMvcAutoConfiguration中的三个关键点。通过对SpringBoot错误处理机制源码的跟踪,我们可以更深入地了解异常处理的实现细节。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [SpringBoot异常处理](https://blog.csdn.net/Linging_24/article/details/126077782)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [SpringBoot 异常处理详解](https://blog.csdn.net/qq_42402854/article/details/91415966)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值