在项目开发中,我们通常会约定一个接口返回的数据格式,其中包含code
,message
,data
等信息,像下面这样:
{
"code": 0,
"message": "success",
"data": ""
}
没有异常的情况,正常返回数据
接口数据对应的实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ResultVo implements Serializable {
private Integer code ;
private String message ;
private Object data ;
}
用于生成Result实体的工具类
public class ResultUtils {
/**
* 成功时返回的方法(data不为空)
*/
public static ResultVo success(Object o) {
return new ResultVo(ResultEnum.SUCCESS.getCode(), ResultEnum.SUCCESS.getMessage(), o);
}
/**
* 成功时返回的方法(data为空)
*/
public static ResultVo success() {
return success(null);
}
/**
* 失败时返回的方法
*/
public static ResultVo error(Integer code, String message) {
return new ResultVo(code, message,null);
}
}
封装code和message的枚举类
可以约定更多的code和对应的message封装成枚举类,以便不同情况下灵活处理
public enum ResultEnum {
UNKUOW_ERROR(-1,"未知错误"),
SUCCESS(0,"success"),
ERROR(1,"error");
private int code;
private String message;
ResultEnum(int code ,String message) {
this.code = code;
this.message = message;
}
//………省略getter and setter
}
在Controller中返回
在Controller中就可以调用ResultUtils的方法进行返回了
@RequestMapping("/test")
public ResultVo test(){
User user = new User();
user.setName("lisi");
user.setId(1);
return ResultUtils.success(user);
}
但是这样在每个请求方法代码中都要对返回数据进行处理,请求返回的返回值也都是ResultVo
,不能方便的看到每个方法的返回值。我们使用AOP切面对Controller中的方法进行封装来解决这个问题
统一返回值切面
@ControllerAdvice
public class MyResponseAdvice implements ResponseBodyAdvice<Object> {
@Autowired
ObjectMapper objectMapper;
//判断是否要执行beforeBodyWrite方法,true为执行,false不执行. 通过该方法可以选择哪些类或那些方法的response要进行处理, 其他的不进行处理.
@Override
public boolean supports(MethodParameter returnType, Class converterType) {
return true;
}
//对response方法进行具体操作处理
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
//返回类型是否已经封装
if (body instanceof ResultVo){
return body;
}
// String特殊处理
if (body instanceof String) {
try {
return objectMapper.writeValueAsString(ResultUtils.success(body));
} catch (JsonProcessingException e) {
return ResultUtils.error(500,"失败");
}
}
return ResultUtils.success(body);
}
}
有异常的情况,统一异常处理
自定义异常类
自定义异常类,继承RuntimeException
。为方便使用可以使用枚举类列举出常见的异常情况,这里直接使用了上面的枚举类。
@Data
@NoArgsConstructor
@AllArgsConstructor
public class BusinessException extends RuntimeException{
private int code;
private String msg;
public BusinessException(ResultEnum resultEnum) {
this.code = resultEnum.getCode();
this.msg = resultEnum.getMessage();
}
}
自定统一异常处理器
@ControllerAdvice
public class BusinessExceptionHandler {
@ResponseBody
@ExceptionHandler({BusinessException.class})
public ResultVo exceptionHandler(BusinessException exception){
// 省略记录日志
return new ResultVo(exception.getCode(), exception.getMsg(), null);
}
@ResponseBody
@ExceptionHandler({Exception.class})
public ResultVo exceptionHandler(Exception exception){
// 省略记录日志
return new ResultVo(ResultEnum.UNKUOW_ERROR.getCode(), ResultEnum.UNKUOW_ERROR.getMessage(), exception.getMessage());
}
}
在Service层直接抛出异常
@Override
public User test(String str) {
if (Objects.isNull(str)){
throw new BusinessException(ResultEnum.ERROR);
}
return new User();
}
@RequestMapping("/test")
public User test(){
User user = userService.test(null);
return user;
}