spring 3.2中,新增了@ControllerAdvice 注解,用于定义@ExceptionHandler
、@InitBinder
、@ModelAttribute
,并应用到所有@RequestMapping
中。
如果全部异常处理返回json,那么可以使用 @RestControllerAdvice 代替 @ControllerAdvice ,这样在方法上就可以不需要添加 @ResponseBody。
当不做任何处理时,默认的异常返回格式:
{
"timestamp": "2021-07-28T08:30:03.641+00:00",
"status": 500,
"error": "Internal Server Error",
"message": "",
"path": "/advice/test"
}
当我们希望返回跟接口正常返回的json数据相同的数据结构时,就需要用的@RestControllerAdvice 。
定义一个返回对象ResultVO
public class ResultVO {
public int status;
public String message;
public Object data;
public ResultVO(int status, String message) {
this.status = status;
this.message = message;
}
public ResultVO(Object data) {
this.status = 200;
this.message = "success";
this.data = data;
}
}
自定义异常处理类CustomerException
public class CustomerException extends RuntimeException{
private String msg;
private Integer code;
public CustomerException( Integer code,String msg) {
this.msg = msg;
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
}
自定义全局异常处理类GlobalExceptionHandler
@RestControllerAdvice
public class GlobalExceptionHandler {
/**
* 全局异常处理
*/
@ExceptionHandler(Exception.class)
public ResultVO exceptionListener(Exception e){
return new ResultVO(500,e.toString());
}
/**
* 自定义异常处理
*/
@ExceptionHandler(CustomerException.class)
public ResultVO exceptionListener(CustomerException e){
return new ResultVO(e.getCode(),e.getMsg());
}
/**
* 应用到所有@RequestMapping注解方法,在其执行之前初始化数据绑定器
*/
@InitBinder
public void initBinder(WebDataBinder binder) {}
/**
* 把值绑定到Model中,使全局@RequestMapping可以获取到该值
*/
@ModelAttribute
public void addAttributes(Model model) {}
}
Controller中异常测试
@RequestMapping("/test")
public ResultVO testAdvice(){
throw new RuntimeException();
}
测试结果
{
"status": 500,
"message": "java.lang.RuntimeException",
"data": null
}
@RequestMapping("/test")
public ResultVO testAdvice(){
//可以定义一个异常枚举传参
throw new CustomerException(400,"测试自定义异常");
}
测试结果
{
"status": 400,
"message": "测试自定义异常",
"data": null
}
如果在controller层中只关心业务数据,而不去关心数据的形式。 就要通过实现ResponseBodyAdvice接口来统一处理报文。
将上面的GlobalExceptionHandler 重命名为GlobalResponseHandler实现 ResponseBodyAdvice接口
@RestControllerAdvice
public class GlobalResponseHandler implements ResponseBodyAdvice<Object> {
/**
* 全局异常处理
*/
@ExceptionHandler(Exception.class)
public ResultVO ExceptionListener(Exception e) {
return new ResultVO(500, e.toString());
}
/**
* 自定义异常处理
*/
@ExceptionHandler(CustomerException.class)
public ResultVO ExceptionListener(CustomerException e) {
return new ResultVO(e.getCode(), e.getMsg());
}
/**
* 应用到所有@RequestMapping注解方法,在其执行之前初始化数据绑定器
*/
@InitBinder
public void initBinder(WebDataBinder binder) {
}
/**
* 把值绑定到Model中,使全局@RequestMapping可以获取到该值
*/
@ModelAttribute
public void addAttributes(Model model) {
}
//返回true执行beforeBodyWrite,返回false不执行
@Override
public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {
return true;
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter methodParameter, MediaType mediaType,
Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest serverHttpRequest,
ServerHttpResponse serverHttpResponse) {
if (!(body instanceof ResultVO)) {
return new ResultVO(body);
}
return body;
}
}
当返回string类型的时候,HttpMessageConverter用的是StringHttpMessageConverter,这个时候就会报类型转换异常 ,ResultVO转换String失败。
解决方法:当返回string的时候,将RusultVO对象转换成json字符串返回
@Override
public Object beforeBodyWrite(Object body, MethodParameter methodParameter, MediaType mediaType,
Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest serverHttpRequest,
ServerHttpResponse serverHttpResponse) {
if (!(body instanceof ResultVO)) {
if (body instanceof String) {
return JSON.toJSONString(new ResultVO(body));
}
return new ResultVO(body);
}
return body;
}
如果某个接口想返回其他类型的对象,就需要supports 方法返回false,此时可以定义一个注解@IgnoreSupport
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface IgnoreSupport {
}
在不需要封装返回数据的controller上使用@IgnoreSupport,supports方法中的MethodParameter 参数能获取到该注解即返回false,否则返回true。
修改后的supports方法:
@Override
public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {
return !methodParameter.hasMethodAnnotation(IgnoreSupport.class);
}