springboot全局的异常拦截处理
一、为什么使用 Validation 来验证参数 通常我们在使用spring框架编写接口时,对于部分接口的参数我们要进行判空或者格式校验来避免程序出现异常。那是我们一般都是使用if-else逐个对参数进行校验。这种方法按逻辑来说也是没有问题的,同样也能实现预期效果。但是,这样的代码从可读性以及美观程序来看,是非常糟糕的。那么,我们就可以使用@valid注解来帮助我们优雅的校验参数。二
1 全局异常处理与HttpServletResponse响应
@RestControllerAdvice
是帮助我们把信息转成json格式返回
@ResponseBody
是将方法中的字符串转成json格式同一返回,一般该方法返回值为Object
1.1 使用@RestControllerAdvice搭配@ExceptionHandler(推荐)
全局异常处理类只需要在类上标注@RestControllerAdvice,并在处理相应异常的方法上使用@ExceptionHandler注解,写明处理哪个异常即可。
可以在方法参数中添加HttpServletResponse参数,spring会自动帮助我们获取,然后我们拦截到具体错误信息就可以直接返回
@RestControllerAdvice
public class GlobalControllerAdvice {
private static final String BAD_REQUEST_MSG = "客户端请求参数错误";
@ExceptionHandler(value = {IllegalArgumentException.class})
public ResponseEntity<Object> handleIllegalArgumentException(IllegalArgumentException ex) {
return new ResponseEntity<>(ex.getMessage(), HttpStatus.BAD_REQUEST);
}
// <1> 处理 form data方式调用接口校验失败抛出的异常
@ExceptionHandler(BindException.class)
public ResultInfo bindExceptionHandler(BindException e) {
List<FieldError> fieldErrors = e.getBindingResult().getFieldErrors();
List<String> collect = fieldErrors.stream()
.map(o -> o.getDefaultMessage())
.collect(Collectors.toList());
return new ResultInfo().success(HttpStatus.BAD_REQUEST.value(), BAD_REQUEST_MSG, collect);
}
// <2> 处理 json 请求体调用接口校验失败抛出的异常
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResultInfo methodArgumentNotValidExceptionHandler(HttpServletResponse httpServletResponse,MethodArgumentNotValidException e) {
List<FieldError> fieldErrors = e.getBindingResult().getFieldErrors();
List<String> collect = fieldErrors.stream()
.map(o -> o.getDefaultMessage())
.collect(Collectors.toList());
return new ResultInfo().success(HttpStatus.BAD_REQUEST.value(), BAD_REQUEST_MSG, collect);
}
// <3> 处理单个参数校验失败抛出的异常
@ExceptionHandler(ConstraintViolationException.class)
public ResultInfo constraintViolationExceptionHandler(ConstraintViolationException e) {
Set<ConstraintViolation<?>> constraintViolations = e.getConstraintViolations();
List<String> collect = constraintViolations.stream()
.map(o -> o.getMessage())
.collect(Collectors.toList());
return new ResultInfo().success(HttpStatus.BAD_REQUEST.value(), BAD_REQUEST_MSG, collect);
}
@ExceptionHandler(value = Exception.class)
@ResponseBody
public Object handle(Exception e) {
logger.error(e.getMessage(), e);
Map<String,Object> map = new HashMap<>();
if (e instanceof MyException) {
MyException myException = (MyException) e;
map.put("code",500);
map.put("msg",myException.getMessage());
return map;
} else {
e.printStackTrace();
map.put("code",500);
map.put("msg","出错啦");
return map;
}
}
事实上,在全局异常处理类中,我们可以写多个异常处理方法,以下总结了三种参数校验时可能引发的异常:
-
使用form data方式调用接口,校验异常抛出
BindException
-
使用 json 请求体调用接口,校验异常抛出
MethodArgumentNotValidException
-
单个参数校验异常抛出
ConstraintViolationException
注:单个参数校验需要在参数上增加校验注解,并在类上标注@Validated。
全局异常处理类可以添加各种需要处理的异常,比如添加一个对Exception.class的异常处理,当所有ExceptionHandler都无法处理时,由其记录异常信息,并返回友好提示。
1.2 继承自ResponseEntityExceptionHandler搭配@RestControllerAdvice
注意:过滤器中的异常无法被拦截
@RestControllerAdvice
@Order(80)
@Slf4j
public class ValidationExceptionHandle extends ResponseEntityExceptionHandler {
@Override
protected ResponseEntity<Object> handleBindException(BindException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
logger.error(ex.getMessage());
return validExceptionCommon(ex.getBindingResult());
}
@Override
protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
logger.error(ex.getMessage());
return validExceptionCommon(ex.getBindingResult());
}
/**
* 校验异常统一返回格式
* @param result
* @return
*/
private ResponseEntity<Object> validExceptionCommon(BindingResult result){
ResultVo<Object> resultVo = new ResultVo<>();
if (result.hasErrors()) {
List<ObjectError> errors = result.getAllErrors();
for (ObjectError error : errors) {
FieldError fieldError = (FieldError) error;
resultVo.setCode(500);
resultVo.setMsg(fieldError.getDefaultMessage());
}
}
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(resultVo);
}
}
2.ExceptionResolver与@ControllerAdvice(@RestControllerAdvice)的选择
在基于Spring框架的项目中,可以通过在ApplicationContext-MVC.xml(即SpringMVC配置)文件中配置 ExceptionResolver 的bean ,来配置 全局捕获异常处理 类,然后自定义异常处理类处理。注意如果是spring配置文件中定义过的ExceptionResolver 类,不需要添加@Component。如果是SpringBoot 则需要。这是因为springboot没有自定义配置全局异常捕获类,所以需要添加@Component,来标识该类为Bean。
异常处理可以分为三种。
第一种是进入@Controller标识的方法前 产生的异常,例如URL地址错误。这种异常处理需要 异常处理类通过实现 ErrorController 来处理。
第二种是进入Controller时,但还未进行逻辑处理时 产生的异常,例如传入参数数据类型错误。这种异常处理需要用@ControllerAdvice标识并处理,建议继承 ResponseEntityExceptionHandler 来处理,该父类包括了很多已经被@ExceptionHandler 注解标识的方法,包括一些参数转换,请求方法不支持等类型等等。
第三种时进入Controller,进行逻辑处理时产生的异常,例如NullPointerException异常等。这种异常处理也可以用@ControllerAdvice来标识并进行处理,也建议继承ResponseEntityExceptionHandler 处理, 这里我们可以用@ExceptionHandler 自定义捕获的异常并处理。
以上三种情况都是restful的情况,结果会返回一个Json。
-
如果希望返回跳转页面,则需要实现HandlerExceptionResolver类来进行异常处理并跳转。
-
注意@ControllerAdvice需要搭配@ExceptionHandler来使用,自定义捕获并处理异常。
-
@ControllerAdvice一样可以做页面跳转,返回String不要加@ResponseBody
更多推荐
所有评论(0)