一、需求背景
1.在web接口开发时,每个模块接口返回数据格式需做封装,如果每个模块的返回格式不同,前端需要最不同数据格式的适配,使得整个项目很杂乱。如果对接口的返回数据做统一封装就解决此类问题。
2.项目的运行时异常,例如用户名错误此类的提示性异常,需要对此类异常做统一性封装。
二、方法
解决方法
问题一:使用@ControllerAdvice注解,以及实现ResponseBodyAdvice接口,从而实现拦截后再封装逻辑
问题二:使用@ControllerAdvice和@ExceptionHandler注解配合使用,从而捕获自定义异常类,实现逻辑的再处理。
三、具体实现
/**
*封装的接口统一返回对象
*/
@Data
public class ResultPo<T> {
private int code;
private String msg;
private T data;
public static<T> ResultPo<T> success(T t){
ResultPo<T> resultPo = new ResultPo<>();
resultPo.setCode(ResponseCodeEnum.SUCCESS.getCode());
resultPo.setMsg(ResponseCodeEnum.SUCCESS.getMessage());
resultPo.setData(t);
return resultPo;
}
public static ResultPo error(int code,String msg){
ResultPo resultPo = new ResultPo();
resultPo.setCode(code);
resultPo.setMsg(msg);
return resultPo;
}
}
// 自定义异常类
@Getter
public class BusinessException extends RuntimeException{
private String msg;
private int code;
public BusinessException(String msg){
super(msg);
this.msg =msg;
this.code = ResponseCodeEnum.FAIL.getCode();
}
public BusinessException(String msg,int code){
super(msg);
this.msg =msg;
this.code = code;
}
}
@ResponseBody
@ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice<Object> {
Logger log = LoggerFactory.getLogger(ResponseAdvice.class);
// 自定义异常捕获
@ExceptionHandler(BusinessException.class)
public ResultPo customException(BusinessException bs){
ResultPo resultPo = new ResultPo();
resultPo.setCode(bs.getCode());
resultPo.setMsg(bs.getMsg());
resultPo.setData(null);
return resultPo;
}
/**
*return turn 代表此接口会进入beforeBodyWrite方法,进行逻辑处理
*/
@Override
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
return !returnType.getDeclaringClass().getName().contains("springfox");
// return true;
}
@Nullable
@Override
public Object beforeBodyWrite(@Nullable Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
// 如果接口返回为string类型,则需要单独处理
if(returnType.getGenericParameterType().equals(String.class)){
ObjectMapper objectMapper = new ObjectMapper();
try {
HttpHeaders headers = response.getHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
return objectMapper.writeValueAsString(ResultPo.success(body));
} catch (JsonProcessingException e) {
e.printStackTrace();
}
}
// 接口出现自定义异常,已被customException方法捕获,则无需再次封装
if(returnType.getGenericParameterType().equals(ResultPo.class)){
return body;
}
return ResultPo.success(body);
}
}
四、遇到的问题
1.当项目中使用swagger时,会拦截swagger的相关请求
出现的原因:@ControllerAdvice注解会拦截@Controller以及@RestController标记的接口,而swagger的接口是web接口,也是带有@RestController相关接口
解决方法:
1.对swagger进行放行,就是上面代码中那样,放行带有springfox字样的接口
@Override
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
return !returnType.getDeclaringClass().getName().contains("springfox");
// return true;
}
2.自定义一个注解,在需要统一返回值的接口上添加上自定义注解,然后@ControllerAdvice根据自定义注解进行拦截
// ResponseResult为自定义注解
@ControllerAdvice(annotations = ResponseResult.class)