前言
本文主要介绍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());
}
}
**源码地址: