赞
踩
后端给前端的数据类型可能会是基本数据类型、String字符串、对象、数组、或者异常提示等。前端拿到你返回的数据去展示或者给出错误提示,但他不可能说每个接口都把这些异常提示处理一遍,比如说返回没有登录、或者一些业务异常等。
基于上面场景,那么我们要做的就是在后端返回结果前做一层统一处理。返回一个统一的对象,如ResponseVO,有code、msg、data;前端根据返回的code做统一处理
好那么接下来就是基本的实现。
@RequestMapping("/test")
public Map<String,Object> test(){
Map<String,Object> map = new HashMap<>();
map.put("code","0");
map.put("msg","成功");
map.put("data","测试");
return map;
}
返回结果:
上面图片我们可以看到,满足了我们的需求,返回了code、msg、还有我们的数据data。
但是问题来了,我们每个方法都要写一遍map,把这些数据放进去是不是很麻烦呢,在上面花这么多时间去写这个还怎么摸鱼呢,因此我们小小的优化一下就有了我们的进阶版
@Data public class ResponseVO implements Serializable { /** * 响应状态码,0-成功,非0-失败 */ private Integer code = 0; /** * 返回结果说明 */ private String msg = "成功"; /** * JSON格式响应数据 */ private Object data; /** * 返回成功 * @param data * @return */ public static ResponseVO success(Object data){ ResponseVO response = new ResponseVO(); response.setCode(0); response.setMsg("成功"); response.setData(data); return response; } }
这时候在controller调用就变成了下面这样,是不是简洁多了呢
@RequestMapping("/test1")
public ResponseVO test1(){
return ResponseVO.success("测试1");
}
现在虽然简洁多了,但是还是在每个方法上都要写ResponseVO.success()或者ResponseVO.fail(),而且每个方法的返回值都变成了ResponseVO,我们都不知道他们的意义了,那有没有统一处理的呢,就是我该返回啥就返回啥,controller层不用关心这些?答案当然是有的,因此就有了下面的最终版方案。
3. 最终版,ResponseBodyAdvice
接下来就要用到ResponseBodyAdvice,从字面意思理解它的意思就是返回体切面,就是对Controller返回的数据进行统一处理,因此我们只要实现这个接口,在上面做统一处理即可,他有两个接口,我们只需在beforeBodyWrite方法处理就可以了,唯一要注意的就是当返回String类型时要特殊处理,不然会报转换错误。
@RestControllerAdvice public class ResponseHandler implements ResponseBodyAdvice<Object> { private Log log = LogFactory.getLog(ResponseHandler.class); @Override public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) { return true; } @Override public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) { ResponseVO respVo = null; if (body instanceof ResponseVO) { respVo = (ResponseVO) body; }else { respVo = new ResponseVO(); respVo.setData(body); } //如果返回的字符串类型,会先判断HttpMessageConverter能否支持对应的返回类型再使用ResponseBodyAdvice进行封装 //那么此时在进来就不是String类型,所以会报无法转换成ResponseVO对象,那么这里有两种方法,一种是直接返回json字符串,另一种是 //一种是自己的WebConfig进行额外的配置 if (body instanceof String){ return JSONUtil.toJsonStr(respVo); } return respVo; } }
好了,统一封装后我们就不用去关心返回类型了。
1、假如有个接口特殊,不需要这个返回这个格式怎么办呢?
2、返回的业务异常是否也可以统一处理呢?
我们可以用到ResponseBodyAdvice接口的另一个方法,让你的方法返回值不走这个统一返回格式处理,最好的方式就是定一个注解,在需要忽略的方法上加上这个注解,实现方式如下
1、定义注解IgnoreResponseHandler
@Documented
@Inherited
@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface IgnoreResponseHandler {
}
2、在ResponseBodyAdvice的supports方法忽略
@RestControllerAdvice
public class ResponseHandler implements ResponseBodyAdvice<Object> {
private Log log = LogFactory.getLog(ResponseHandler.class);
@Override
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
return !returnType.hasMethodAnnotation(IgnoreResponseHandler.class);
}
}
使用
@RequestMapping("/test5")
@IgnoreResponseHandler
public String test5(){
return "测试1";
}
返回结果,这样就忽略掉了
对于异常我们想统一处理,就要用到@ExceptionHandler(value = Exception.class)这个注解了,加上这个注解,当抛出异常时都会进这个方法
@ExceptionHandler(value = Exception.class)
public ResponseVO onException(HttpServletRequest request, Exception ex) {
ResponseVO resp = null;
if (ex instanceof AppException) {
resp = new ResponseVO((AppException) ex);
} else {
AppException exception = new AppException(9999,"未知异常");
resp = new ResponseVO(exception);
log.error("未知异常:", ex);
}
return resp;
}
使用
@RequestMapping("/test3")
public String test3(){
throw new AppException("测试异常");
}
好了这样就可以了,当我们在业务实现是抛出业务异常,返回的异常就会统一处理了。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。