springboot全局异常捕获
# springboot 全局异常捕获
本文讲述 springboot 项目如何支持全局异常捕获。系统的很多处代码逻辑都可能出现异常,但是每处都用 try catch 捕获,不仅冗余代码过多,也影响代码的美观,更不利于维护。若不进行异常捕获,springboot 会自动将异常信息吐到响应数据中,对用户非常不友好。那么,可以通过全局异常捕获,来避免上述问题。
提示
本文介绍要捕获的异常是指接口处理逻辑中发生的异常,如定时任务等处发生的异常不会被捕获,因为定时任务的逻辑并非通过接口触发。
若不对系统做全局异常捕获,那么有如下缺点:
- 接口返回的信息不友好
- 接口出现异常时, 响应的数据并不会以接口定义的统一数据格式返回, 不利于前端对接
在未做全局异常捕获时, 异常接口返回的数据格式如下:
{
"timestamp": "2020-09-11 15:34:29",
"status": 400,
"error": "Bad Request",
"errors": [
{
"codes": [
"NotBlank.addFeedbackDto.content",
"NotBlank.content",
"NotBlank.java.lang.String",
"NotBlank"
],
"arguments": [
{
"codes": [
"addFeedbackDto.content",
"content"
],
"arguments": null,
"defaultMessage": "content",
"code": "content"
}
],
"defaultMessage": "内容不能为空",
"objectName": "addFeedbackDto",
"field": "content",
"rejectedValue": "",
"bindingFailure": false,
"code": "NotBlank"
}
],
"message": "Validation failed for object='addFeedbackDto'. Error count: 1",
"path": "/api/user/feedback/addFeedback"
}
既然如此,那么我们需要为系统定义全局异常捕获处理器。
# 1. 创建全局异常捕获处理器
实现全局异常捕获非常简单, 只需要定义如下一个类即可
package com.ruiboyun.facehr.exception;
import cn.hutool.core.exceptions.ExceptionUtil;
import com.ruiboyun.facehr.common.entity.Result;
import lombok.extern.log4j.Log4j2;
import org.springframework.validation.BindException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import javax.validation.ValidationException;
import java.util.StringJoiner;
/**
* 全局异常处理器
*/
@RestControllerAdvice
@Log4j2
public class GlobalExceptionHandler {
/**
* 参数绑定错误
*
* @param ex
* @return
*/
@ExceptionHandler(BindException.class)
public Result handleBindException(BindException ex) {
StringJoiner sj = new StringJoiner(";");
ex.getBindingResult().getFieldErrors().forEach(x -> sj.add(x.getDefaultMessage()));
log.error("参数绑定错误, error[{}]", ExceptionUtil.stacktraceToOneLineString(ex));
return Result.error(sj.toString());
}
/**
* 参数校验错误
*
* @param ex
* @return
*/
@ExceptionHandler(ValidationException.class)
public Result handleValidationException(ValidationException ex) {
log.error("参数校验错误, error[{}]", ExceptionUtil.stacktraceToOneLineString(ex));
return Result.error(ex.getCause().getMessage());
}
/**
* 字段校验不通过异常
*
* @param ex
* @return
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
public Result handleMethodArgumentNotValidException(MethodArgumentNotValidException ex) {
StringJoiner sj = new StringJoiner(";");
ex.getBindingResult().getFieldErrors().forEach(x -> sj.add(x.getDefaultMessage()));
log.error("字段校验不通过异常, error[{}]", ExceptionUtil.stacktraceToOneLineString(ex));
return Result.error(sj.toString());
}
/**
* Controller参数绑定错误
*
* @param ex
* @return
*/
@ExceptionHandler(MissingServletRequestParameterException.class)
public Result handleMissingServletRequestParameterException(MissingServletRequestParameterException ex) {
log.error("Controller参数绑定错误, error[{}]", ExceptionUtil.stacktraceToOneLineString(ex));
return Result.error(ex.getMessage());
}
/**
* 处理方法不支持异常
*
* @param ex
* @return
*/
@ExceptionHandler(value = HttpRequestMethodNotSupportedException.class)
public Result handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException ex) {
log.error("处理方法不支持异常, error[{}]", ExceptionUtil.stacktraceToOneLineString(ex));
return Result.error("HTTP方法错误");
}
/**
* 其他未知异常
*
* @param ex
* @return
*/
@ExceptionHandler(value = Exception.class)
public Result handleException(Exception ex) {
log.error("其他未知异常, error[{}]", ExceptionUtil.stacktraceToOneLineString(ex));
return Result.error("未知异常");
}
}
提示
@RestControllerAdvice 注解相当于@ControllerAdvice + @ResponseBody,简化了注解的数量
那么,现在系统已经具备全局异常捕获的功能。现在访问任何一个接口, 无论接口是否出现异常, 都会返回统一的数据格式,且友好的提示异常信息。
{
"code": 1,
"msg": "内容不能为空",
"data": null
}
# 2. 自定义异常
您可以自定义异常类, 实现在系统逻辑的任何一个位置抛出自定义异常, 交给全局异常处理器。 若想让全局异常处理器捕获自定义异常, 那么需要仿照上面的例子补充一个用来捕获自定义异常的方法。
上次更新: 2021-03-24 17:35:20