返回值全局拦截
前言
现在项目中,软件开发采用前后端分离模式是越来越普遍,后端人员更加专注数据,前端(包括html和app开发人员)更加注重数据展示和部分前端逻辑控制。目前跟前端人员约定的数据返回格式如下:
{
"code": "200",
"message": "成功",
"data": T
}
我们生成一个实体类,命名BaseResponse,如下:
@Data
public class BaseResponse<T>(){
private int code;
private String message;
private T data;
}
无全局拦截
代码逻辑很混乱,重用性很低…
下面的代码基于SpringBoot1.0的框架。跟spring+springmvc差别不大。
这是controller层:
/**
* function :用户控制器
* @author :mayao
* @date :2018/4/14
*/
@RestController
@RequestMapping("/user")
public class UserCtrl extends BaseCtrl {
@Autowired
private IUserService iUserService;
@PostMapping("/add")
public BaseResponse<Integer> addUser(User user){
return iUserService.addUser(user);
}
}
1.一直清楚ctrl里面不能里面有太多逻辑,所以在service层进行了数据的封装处理,那么问题来了,我service的返回结果都是BaseResponse…如下,返回的数据结构辨识度就下降了不少:
public interface IUserService {
BaseResponse<Integer> addUser(User user);
BaseResponse<List<User>> userList(User user);
int updateUserInfo(User user);
}
2.我的service实现里面还要判断各种逻辑业务逻辑混乱,并封装,是不是有种吐血的冲动….,如下:
@Service
public class UserServiceImpl implements IUserService {
@Autowired
private UserMapper userMapper;
@Override
public BaseResponse<Integer> addUser(User user) {
BaseResponse baseResponse = new BaseResponse();
//判断参数缺失
if(StringUtils.isEmpty(user.getUserName())){
baseResponse.setCode(40001);
baseResponse.setMessage("参数缺失");
baseResponse.setData(0);
return baseResponse;
}
//异常捕捉
int code;
String msg;
int data=0;
try{
data = userMapper.addUser(user);
code=200;
msg="成功";
}catch (Exception e){
code=500;
msg="服务器异常";
}
baseResponse.setCode(code);
baseResponse.setMessage(msg);
baseResponse.setData(data);
return baseResponse;
}
}
3.更有甚者,不按约定来的也有,不是返回的BaseResponse,返回其他类型的(第一种)。
4.不易于扩展。后面数据结构需要调整的话,改起来也是费神。
5.其他…
注:隐去了部分相关注释,其实有些重复代码也可以再封装一下什么的,就不多去追究了,重点不在那。上面的代码点夸张的嫌疑,但是的确有这种接口开发的存在。虽然我不想承认几年前我真的干过,那时候前后端分离开发还没这么普及….
采用全局拦截
统一拦截,大家其实很容易想到的是AOP和拦截器做,其实更倾向于AOP,其实是可以做的。但是!Spring已经提供了一个类用于处理这种事情,就是:ResponseBodyAdvice,全包名:org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;意思很明确:返回体建议。
就直接上代码了:
@RestController
@RequestMapping("/user")
public class UserCtrl extends BaseCtrl {
@Autowired
private IUserService iUserService;
@PostMapping("/add")
public int addUser(User user){
return iUserService.addUser(user);
}
@PostMapping("/list")
public BaseResponse<List<User>> userList(User user){
return iUserService.userList(user);
}
}
@Service
public class UserServiceImpl implements IUserService {
@Autowired
private UserMapper userMapper;
@Override
public int addUser(User user) {
return userMapper.addUser(user);
}
@Override
public BaseResponse<List<User>> userList(User user) {
BaseResponse<List<User>> baseResponse = new BaseResponse<>();
baseResponse.setCode(200);
baseResponse.setData(userMapper.userList(user));
baseResponse.setMessage("成功");
return baseResponse;
}
}
拦截返回体:
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
/**
* function :返回值拦截自定义
* @author :mayao
* @date :2018/4/14
*/
@Slf4j
@ControllerAdvice(basePackages = { "com.mayao.ctrls"})
public class GlobalResponseHandler implements ResponseBodyAdvice<Object> {
@Override
public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {
//判断支持的类型,因为我们定义的BaseResponse 里面的data可能是任何类型,这里就不判断统一放过
//如果你想对执行的返回体进行操作,可将上方的Object换成你自己的类型
return true;
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter methodParameter, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest serverHttpRequest,
ServerHttpResponse serverHttpResponse) {
//注解@Slf4j所带,属于非常推荐的lombok项目,可以去看看我的idea插件去了解一下
log.info("请求返回数据类型class={}",body.getClass().getName());
BaseResponse result=null;
//兼容原来的接口返回
if(body instanceof BaseResponse){
result=(BaseResponse)body;
}else{
result=new BaseResponse();
if(null!=body&&!"".equals(body)){
result.setCode(200);
result.setMessage("成功");
result.setData(body);
}
}
if(log.isDebugEnabled()){
log.debug("响应参数:{} ", JSONUtil.toJson(result));
}
return result;
}
}
末:
这里,全局统一拦截返回兼容了原来的老接口的返回。
至于接口异常的情况。异常的自定义,及异常的全局处理就是必不可少了的。
自定义异常及异常全局处理;