SpringBoot全局异常处理+后端统一返回(附源码)

本文介绍了在SpringBoot中如何实现前后端分离的开发模式下与前端友好的交互,包括定义统一的后端返回格式ResultVo,使用@RestControllerAdvice进行控制器统一处理,以及全局异常捕获和自定义BusinessException。通过这些方法,可以简化接口返回处理和增强异常信息的传递。
摘要由CSDN通过智能技术生成

前言

本文主要介绍SpringBoot前后端分离开发模式下,如何友好与前端进行交互。包含controller层统一返回处理,全局异常捕获。文章只粘出关键代码,源码已上传至github,文末获取。

后端统一返回

定义后端返回格式

public class ResultVo {
 5 
 6     private int code;
 7 
 8     private String msg;
 9 
10     private Object data;
11 
12 
13     // 默认返回成功状态码,数据对象
14     public ResultVo(Object data) {
15         this.code = ResultCode.SUCCESS.getCode();
16         this.msg = ResultCode.SUCCESS.getMsg();
17         this.data = data;
18     }
19 
20     // 返回指定状态码,数据对象
21     public ResultVo(StatusCode statusCode, Object data) {
22         this.code = statusCode.getCode();
23         this.msg = statusCode.getMsg();
24         this.data = data;
25     }
26 
27     public ResultVo(ResultCode status, String msg, Object data) {
28         this.code = status.getCode();
29         this.msg = msg;
30         this.data = data;
31     }
32 
33     public static ResultVo success(Object data) {
34         return new ResultVo(ResultCode.SUCCESS, data);
35     }
36 
37     public static ResultVo fail(ResultCode status, String msg, Object data) {
38         return new ResultVo(status, msg, data);
39     }
40 }

统一状态码

public class ResultVo {
 5 
 6     private int code;
 7 
 8     private String msg;
 9 
10     private Object data;
11 
12 
13     // 默认返回成功状态码,数据对象
14     public ResultVo(Object data) {
15         this.code = ResultCode.SUCCESS.getCode();
16         this.msg = ResultCode.SUCCESS.getMsg();
17         this.data = data;
18     }
19 
20     // 返回指定状态码,数据对象
21     public ResultVo(StatusCode statusCode, Object data) {
22         this.code = statusCode.getCode();
23         this.msg = statusCode.getMsg();
24         this.data = data;
25     }
26 
27     public ResultVo(ResultCode status, String msg, Object data) {
28         this.code = status.getCode();
29         this.msg = msg;
30         this.data = data;
31     }
32 
33     public static ResultVo success(Object data) {
34         return new ResultVo(ResultCode.SUCCESS, data);
35     }
36 
37     public static ResultVo fail(ResultCode status, String msg, Object data) {
38         return new ResultVo(status, msg, data);
39     }
40 }

至此,已经基本实现了与前端对接,但是为了避免所有接口都使用new Result()手动封装返回值,还需进一步优化。SpringBoot提供了@RestControllerAdvice注解来统一管理controller层,默认管理全部Controller,可通过basePackages属性精确控制。

主要代码逻辑

@RestControllerAdvice(basePackages = "com.ls.code.valid")
public class BaseResponseBodyAdvice implements ResponseBodyAdvice<Object> {

    @Autowired
    private ObjectMapper objectMapper;


    /**
     * @param methodParameter 方法返回的类型
     * @param converterType   参数类型装换
     * @return 返回true 则调用 beforeBodyWrite方法
     */
    @Override
    public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> converterType) {
        //返回类型为ResultVo 或者带有IgnoreResponseAdvice注解 则不进行包装
        return !(methodParameter.getParameterType().isAssignableFrom(ResultVo.class)
                || methodParameter.hasMethodAnnotation(IgnoreResponseAdvice.class));
    }

    @Override
    @SneakyThrows
    public Object beforeBodyWrite(Object data, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        //String类型返回值单独处理
        if (data instanceof String) {
            return objectMapper.writeValueAsString(new ResultVo(data));
        }
        return ResultVo.success(data);
    }
}

代码说明:如上代码增加了对ResultVo的过滤和 带有@IgnoreResponseAdvice(自定义的注解)注解的过滤,并对String类型的返回值进行了特殊处理。如果不处理会报错
1、由于是公共的配置,可能某些开发还会再controller中写new ResutVo()手动封装,如果不进行过滤则会出现嵌套的现象。如图

2、业务中可能有需求为 只返回一个字符串,并不需要和前端交互。如:各种中间件的探活等。只需要返回字符串即可,则需要自定义一个注解,接了此注解的方法则不进行包装。
3、如果返回值类型为String内部报错为java.lang.ClassCastException: com.ls.code.valid.common.ResultVo cannot be cast to java.lang.String,需要对String类型的返回值进行特殊处理。

全局异常处理

Springboot提供@ControllerAdvice注解,可以进行全局异常定制。我们所需处理的异常无非三种类型。

1、各种参数校验异常,例:账号密码不能为空等 代码以spring-boot-starter-validation为例。
2、自定义业务异常,开发中,经常要对用户的错误操作进行提示等,需要我们自定义异常,并给出相应的提示 例:商品已下架等。
3、服务器异常,例:空指针 。由于异常种类众多,我们不能对每一种异常都做定制化,所以只对Exception进行封装,异常由开发自己保证,而且这些异常返回给前端并没有什么意义。

自定义异常

@Getter
public class BusinessException extends RuntimeException {
    private Integer code;
    private String msg;

    public BusinessException(ResultCode resultCode, String message) {
        //设置错误详情
        super(message);
        this.code = resultCode.getCode();
        this.msg = resultCode.getMsg();
    }

    public BusinessException(String message) {
        super(message);
        this.code = ResultCode.FAIL.getCode();
        this.msg = ResultCode.FAIL.getMsg();
    }
}

异常定制代码

@ControllerAdvice
@ResponseBody
@Slf4j
public class ControllerExceptionAdvice {

    /**
     * 服务异常捕获
     *
     * @param e
     * @return
     */
    @ExceptionHandler({Exception.class})
    public ResultVo MethodArgumentNotValidExceptionHandler(Exception e) {
        log.error(Throwables.getStackTrace(e));
        return ResultVo.fail(ResultCode.ERROR_500, ResultCode.ERROR_500.getMsg(), e.getMessage());
    }

    /**
     * 参数校验异常捕获
     *
     * @param e
     * @return
     */
    @ExceptionHandler({BindException.class})
    public ResultVo MethodArgumentNotValidExceptionHandler(BindException e) {
        // 从异常对象中拿到ObjectError对象
        ObjectError objectError = e.getBindingResult().getAllErrors().get(0);
        return new ResultVo(ResultCode.ERROR_400, objectError.getDefaultMessage());
    }

    /**
     * 业务异常捕获
     *
     * @param e
     * @return
     */
    @ExceptionHandler({BusinessException.class})
    public ResultVo MethodArgumentNotValidExceptionHandler(BusinessException e) {
        return new ResultVo(e.getCode(), e.getMsg(), e.getMessage());
    }

}

**源码地址:

https://github.com/liushuai2716/code/tree/master/valid-code

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值