后端返回数据全局处理

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/my_nice_life/article/details/79945279

返回值全局拦截

前言

现在项目中,软件开发采用前后端分离模式是越来越普遍,后端人员更加专注数据,前端(包括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;
    }
}

末:
这里,全局统一拦截返回兼容了原来的老接口的返回。
至于接口异常的情况。异常的自定义,及异常的全局处理就是必不可少了的。
自定义异常及异常全局处理;

阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页