SpringBoot优雅的全局异常处理

前言

在日常项目开发中,异常是常见的,但是如何更高效的处理好异常信息,让我们能快速定位到BUG,是很重要的,不仅能够提高我们的开发效率,还能让你代码看上去更舒服,SpringBoot的项目已经有一定的异常处理了,但是对于我们开发者而言可能就不太合适了,因此我们需要对这些异常进行统一的捕获并处理。

SpringBoot默认的错误处理机制

返回错误页面

默认返回 Whitelabel Error Page页面的样式太单调,用户体验不好。 如 果 我 们 需 要 将 所 有 的 异 常 同 一 跳 转 到 自 定 义 的 错 误 页 面 , 需 要 再 src/main/resources/templates 目录下创建 error.html 页面。

注意:名称必须叫 error

1<!DOCTYPE html> 2<html> 3<head> 4<meta charset="UTF-8"> 5<title>Insert title here</title> 6</head> 7<body> 8 <!--SpringBoot默认存储异常信息的key为exception--> 9 <span th:text="${exception}" /> 10</body> 11</html>

返回json格式api

Json格式的结果字符串不统一,与前端人员约定统一格式不一致

源码分析

SpringBoot在页面 发生异常的时候会自动把请求转到/error,SpringBoot内置了一个BasicErrorController对异常进行统一的处理,当然也可以自定义这个路径

1@RequestMapping( 2 produces = {"text/html"} 3 ) 4 public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) { 5 HttpStatus status = this.getStatus(request); 6 Map<String, Object> model = Collections.unmodifiableMap(this.getErrorAttributes(request, this.getErrorAttributeOptions(request, MediaType.TEXT_HTML))); 7 response.setStatus(status.value()); 8 ModelAndView modelAndView = this.resolveErrorView(request, response, status, model); 9 return modelAndView != null ? modelAndView : new ModelAndView("error", model); 10 } 11 12 @RequestMapping 13 public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) { 14 HttpStatus status = this.getStatus(request); 15 if (status == HttpStatus.NO_CONTENT) { 16 return new ResponseEntity(status); 17 } else { 18 Map<String, Object> body = this.getErrorAttributes(request, this.getErrorAttributeOptions(request, MediaType.ALL)); 19 return new ResponseEntity(body, status); 20 } 21 }

我们可以看到刚好对照两个方法一个返回错误页面,一个返回错误字符,默认错误路径是/error如果有自定义就用自定义的

1server.error.path=/custom/error

自定义错误处理

SpringBoot提供了ErrorAttribute类型 自定义ErrorAttribute类型的bean还是默认的两种响应方式,只不过改变了响应内容项而已

1package cn.soboys.core; 2 3 4import cn.hutool.core.bean.BeanUtil; 5import cn.soboys.core.ret.Result; 6import cn.soboys.core.ret.ResultCode; 7import cn.soboys.core.ret.ResultResponse; 8import org.springframework.boot.web.error.ErrorAttributeOptions; 9import org.springframework.boot.web.servlet.error.DefaultErrorAttributes; 10import org.springframework.stereotype.Component; 11import org.springframework.web.context.request.WebRequest; 12import org.springframework.web.servlet.ModelAndView; 13 14import javax.servlet.http.HttpServletRequest; 15import javax.servlet.http.HttpServletResponse; 16import java.util.Map; 17 18/** 19 * @author kenx 20 * @version 1.0 21 * @date 2021/6/18 14:14 22 * 全局错误 23 */ 24@Component 25public class GlobalErrorHandler extends DefaultErrorAttributes { 26 27 28 /** 29 * 自定义错误返回页面 30 * @param request 31 * @param response 32 * @param handler 33 * @param ex 34 * @return 35 */ 36 @Override 37 public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { 38 return super.resolveException(request, response, handler, ex); 39 } 40 41 /** 42 * 自定义错误返回格式 43 * 44 * @param webRequest 45 * @param options 46 * @return 47 */ 48 @Override 49 public Map<String, Object> getErrorAttributes(WebRequest webRequest, ErrorAttributeOptions options) { 50 Map<String, Object> errorAttributes = super.getErrorAttributes(webRequest, options); 51 Result result = ResultResponse.failure(ResultCode.NOT_FOUND, errorAttributes.get("path")); 52 Map map = BeanUtil.beanToMap(result, true, true); 53 return map; 54 } 55}

自定义业务异常类

继承RuntimeException

1package cn.soboys.core.authentication; 2 3import cn.soboys.core.ret.ResultCode; 4import lombok.Data; 5 6/** 7 * @author kenx 8 * @version 1.0 9 * @date 2021/6/22 13:58 10 * 认证异常 11 */ 12@Data 13public class AuthenticationException extends RuntimeException { 14 15 public AuthenticationException(String message) { 16 super(message); 17 } 18 19}

全局捕获异常

通过SpringBoot提供的@RestControllerAdvice@ControllerAdvice 结合@ExceptionHandler使用

@RestControllerAdvice@ControllerAdvice区别和@RestController,@Controller一样如果想返回json格式也可以单独使用@ResponseBody注解在方法上

需要捕获什么异常通过@ExceptionHandler来指定对应异常类就可以了这里原则是按照从小到大异常进行依次执行

通俗来讲就是当小的异常没有指定捕获时,大的异常包含了此异常就会被执行比如Exception 异常包含了所有异常类,是所有异常超级父类,当出现没有指定异常时此时对应捕获了Exception异常的方法会执行

@ExceptionHandler注解处理异常

1@Controller 2public class DemoController { 3 @RequestMapping("/show") 4 public String showInfo() { 5 String str = null; 6 str.length(); 7 return "index"; 8 } 9 10 @RequestMapping("/show2") 11 public String showInfo2() { 12 int a = 10 / 0; 13 return "index"; 14 } 15 16 /** 17 * java.lang.ArithmeticException 该方法需要返回一个 ModelAndView:目的是可以让我们封装异常信息以及视 18 * 图的指定 参数 Exception e:会将产生异常对象注入到方法中 19 */ 20 @ExceptionHandler(value = { java.lang.ArithmeticException.class }) 21 public ModelAndView arithmeticExceptionHandler(Exception e) { 22 ModelAndView mv = new ModelAndView(); 23 mv.addObject("error", e.toString()); 24 mv.setViewName("error1"); 25 return mv; 26 } 27 28 /** 29 * java.lang.NullPointerException 该方法需要返回一个 ModelAndView:目的是可以让我们封装异常信息以及视 30 * 图的指定 参数 Exception e:会将产生异常对象注入到方法中 31 */ 32 @ExceptionHandler(value = { java.lang.NullPointerException.class }) 33 public ModelAndView nullPointerExceptionHandler(Exception e) { 34 ModelAndView mv = new ModelAndView(); 35 mv.addObject("error", e.toString()); 36 mv.setViewName("error2"); 37 return mv; 38 } 39}

优点:可以自定义异常信息存储的key,自定义跳转视图的名称

缺点:需要编写大量的异常处理方法,不能跨controller,如果两个controller中出现同样的异常,需要重新编写异常处理的方法

@ControllerAdvice+@ExceptionHandler 注解处理异常

1/** 2 * @author kenx 3 * @version 1.0 4 * @date 2021/6/17 20:19 5 * 全局异常统一处理 6 */ 7@RestControllerAdvice 8public class GlobalExceptionHandler { 9 /** 10 * 认证异常 11 * @param e 12 * @return 13 */ 14 @ExceptionHandler(AuthenticationException.class) 15 public Result UnNoException(AuthenticationException e) { 16 return ResultResponse.failure(ResultCode.UNAUTHORIZED,e.getMessage()); 17 } 18 19 /** 20 * 21 * @param e 未知异常捕获 22 * @return 23 */ 24 @ExceptionHandler(Exception.class) 25 public Result UnNoException(Exception e) { 26 return ResultResponse.failure(ResultCode.INTERNAL_SERVER_ERROR, e.getMessage()); 27 } 28}

优点:可以自定义异常信息存储的key,自定义跳转视图的名称,跨controller统一拦截统一捕获,一般都是使用这种

关注公众号猿人生了解更多好文 wechart-gongzhonghao

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

一站式统一返回值封装、异常处理、异常错误码解决方案—最强的Sping Boot接口优雅响应处理器 | 京东云技术团队

GracefulResponse是一个SpringBoot体系下的优雅响应处理器,提供一站式统一返回值封装、异常处理、异常错误码等功能。使用GracefulResponse进行web接口开发不仅可以节省大量的时间,还可以提高代码质量,使代码逻辑更清晰。

Spring Cloud Gateway 全局通用异常处理

为什么需要全局异常处理在传统SpringBoot应用中,我们@ControllerAdvice来处理全局的异常,进行统一包装返回//摘至springcloudalibabaconsole模块处理@ControllerAdvicepublicclassConsol

Spring Boot 2.x(七):全局异常处理

前言异常的处理在我们的日常开发中是一个绕不过去的坎,在SpringBoot项目中如何优雅的去处理异常,正是我们这一节课需要研究的方向。异常的分类在一个SpringBoot项目中,我们可以把异常分为两种,第一种是请求到达Controller层之前,第二种是到达Controller层之后项目代码中发生的错误。而第一种又可

03.Android崩溃Crash库之ExceptionHandler分析

目录总结00.异常处理几个常用api01.UncaughtExceptionHandler02.Java线程处理异常分析03.Android中线程处理异常分析04.为何使用setDefaultUncaughtExceptionHandler前沿上一篇整体介绍了crash崩溃