SpringBoot优雅的全局异常处理

前言

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

SpringBoot默认的错误处理机制

返回错误页面

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

注意:名称必须叫 error

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<!--SpringBoot默认存储异常信息的key为exception-->
<span th:text="${exception}" />
</body>
</html>

返回json格式api

Json格式的结果字符串不统一,与前端人员约定统一格式不一致SpringBoot优雅的全局异常处理

源码分析

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

SpringBoot优雅的全局异常处理
@RequestMapping(
produces = {"text/html"}
)
public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
HttpStatus status = this.getStatus(request);
Map<String, Object> model = Collections.unmodifiableMap(this.getErrorAttributes(request, this.getErrorAttributeOptions(request, MediaType.TEXT_HTML)));
response.setStatus(status.value());
ModelAndView modelAndView = this.resolveErrorView(request, response, status, model);
return modelAndView != null ? modelAndView : new ModelAndView("error", model);
}

@RequestMapping
public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
HttpStatus status = this.getStatus(request);
if (status == HttpStatus.NO_CONTENT) {
return new ResponseEntity(status);
} else {
Map<String, Object> body = this.getErrorAttributes(request, this.getErrorAttributeOptions(request, MediaType.ALL));
return new ResponseEntity(body, status);
}
}

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

server.error.path=/custom/error

自定义错误处理

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

package cn.soboys.core;


import cn.hutool.core.bean.BeanUtil;
import cn.soboys.core.ret.Result;
import cn.soboys.core.ret.ResultCode;
import cn.soboys.core.ret.ResultResponse;
import org.springframework.boot.web.error.ErrorAttributeOptions;
import org.springframework.boot.web.servlet.error.DefaultErrorAttributes;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;

/**
* @author kenx
* @version 1.0
* @date 2021/6/18 14:14
* 全局错误
*/

@Component
public class GlobalErrorHandler extends DefaultErrorAttributes {


/**
* 自定义错误返回页面
* @param request
* @param response
* @param handler
* @param ex
* @return
*/

@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
return super.resolveException(request, response, handler, ex);
}

/**
* 自定义错误返回格式
*
* @param webRequest
* @param options
* @return
*/

@Override
public Map<String, Object> getErrorAttributes(WebRequest webRequest, ErrorAttributeOptions options) {
Map<String, Object> errorAttributes = super.getErrorAttributes(webRequest, options);
Result result = ResultResponse.failure(ResultCode.NOT_FOUND, errorAttributes.get("path"));
Map map = BeanUtil.beanToMap(result, true, true);
return map;
}
}

自定义业务异常类

继承RuntimeException

package cn.soboys.core.authentication;

import cn.soboys.core.ret.ResultCode;
import lombok.Data;

/**
* @author kenx
* @version 1.0
* @date 2021/6/22 13:58
* 认证异常
*/

@Data
public class AuthenticationException extends RuntimeException {

public AuthenticationException(String message) {
super(message);
}

}

全局捕获异常

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

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

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

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

@ExceptionHandler注解处理异常

@Controller
public class DemoController {
@RequestMapping("/show")
public String showInfo() {
String str = null;
str.length();
return "index";
}

@RequestMapping("/show2")
public String showInfo2() {
int a = 10 / 0;
return "index";
}

/**
* java.lang.ArithmeticException 该方法需要返回一个 ModelAndView:目的是可以让我们封装异常信息以及视
* 图的指定 参数 Exception e:会将产生异常对象注入到方法中
*/

@ExceptionHandler(value = { java.lang.ArithmeticException.class })
public ModelAndView arithmeticExceptionHandler(Exception e)
{
ModelAndView mv = new ModelAndView();
mv.addObject("error", e.toString());
mv.setViewName("error1");
return mv;
}

/**
* java.lang.NullPointerException 该方法需要返回一个 ModelAndView:目的是可以让我们封装异常信息以及视
* 图的指定 参数 Exception e:会将产生异常对象注入到方法中
*/

@ExceptionHandler(value = { java.lang.NullPointerException.class })
public ModelAndView nullPointerExceptionHandler(Exception e)
{
ModelAndView mv = new ModelAndView();
mv.addObject("error", e.toString());
mv.setViewName("error2");
return mv;
}
}

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

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

@ControllerAdvice+@ExceptionHandler 注解处理异常

/**
* @author kenx
* @version 1.0
* @date 2021/6/17 20:19
* 全局异常统一处理
*/

@RestControllerAdvice
public class GlobalExceptionHandler {
/**
* 认证异常
* @param e
* @return
*/

@ExceptionHandler(AuthenticationException.class)
public Result UnNoException(AuthenticationException e)
{
return ResultResponse.failure(ResultCode.UNAUTHORIZED,e.getMessage());
}

/**
*
* @param e 未知异常捕获
* @return
*/

@ExceptionHandler(Exception.class)
public Result UnNoException(Exception e)
{
return ResultResponse.failure(ResultCode.INTERNAL_SERVER_ERROR, e.getMessage());
}
}

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

原文始发于微信公众号(猿小叔):SpringBoot优雅的全局异常处理

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/28069.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!