Spring Boot API 统一返回格式封装

今天给大家带来的是Spring Boot API 统一返回格式封装,我们在做项目的时候API 接口返回是需要统一格式的,只有这样前端的同学才可对接口返回的数据做统一处理,也可以使前后端分离
模式的开发中,只关注于业务,而无需考虑对每一个返回结果做处理,中心思想是,所有结果封装为json,错误信息通过异常抛出,统一捕捉处理后返回前端,举例:如果插入一条用户信息,而
这个用户已经在系统中存在,则返回插入失败,我们用枚举定义一个返回信息,通过异常抛出。

**说明:本人没有公众号,只要这个博客,也会对博客进行持续更新,如果对您有帮助,希望您持续关注**

源码地址//gitee.com/zgc005/resut

废话少说 上代码

先讲项目结构

 ├── LICENSE
 ├── demo.iml
 ├── pom.xml
 ├── re
 └── src
      ├── main
      │   ├── java
      │   │   └── com
      │   │       └── result
      │   │           └── demo
      │   │               ├── DemoApplication.java
      │   │               ├── common
      │   │               │   └── config
      │   │               ├── global
      │   │               │   ├── advice
      │   │               │   │   ├── ExceptionResultAdvice.java    ———— 异常捕捉。处理。
      │   │               │   │   └── ResponseResultAdvice.java    ———— 接口返回统一封装
      │   │               │   ├── annotation
      │   │               │   │   └── ResponseResult.java     ———— 自定义标签 用来验证接口是否需要包装返回 并且验证接口是否存在
      │   │               │   ├── config
      │   │               │   │   └── GlobalConfig.java   ———— 全局配置,读取*.yml 配置文件信息
      │   │               │   ├── enums
      │   │               │   │   └── ResultCode.java ———— api异常枚举
      │   │               │   ├── exception
      │   │               │   │   └── BizException.java
      │   │               │   ├── filterLists
      │   │               │   │   ├── FilterConfig.java  ———— Filter  配置
      │   │               │   │   └── SignAuthFilter.java  ———— 接口请求验签等操作
      │   │               │   ├── interceptor 
      │   │               │   │   ├── ResponseResultInterceptor.java  用来处理返回数据是否需要封装
      │   │               │   │   └── WebConfigurer.java
      │   │               │   ├── result
      │   │               │   │   ├── EnumInterface.java
      │   │               │   │   └── Result.java
      │   │               │   └── sign
      │   │               │       ├── BodyReaderHttpServletRequestWrapper.java ————对请求流处理
      │   │               │       ├── HttpUtils.java
      │   │               │       └── SignUtil.java
      │   │               ├── modules
      │   │               │   ├── goods
      │   │               │   │   ├── controller
      │   │               │   │   │   └── GoodsController.java
      │   │               │   │   ├── dao
      │   │               │   │   │   └── GoodsDao.java   
      │   │               │   │   ├── entity
      │   │               │   │   │   ├── Goods.java
      │   │               │   │   │   ├── GoodsBrandBean.java
      │   │               │   │   │   ├── GoodsImage.java
      │   │               │   │   │   ├── ProductEntity.java
      │   │               │   │   │   └── TestName.java
      │   │               │   │   └── service
      │   │               │   │       ├── GoodsService.java
      │   │               │   │       └── impl
      │   │               │   │           └── GoodsServiceImpl.java
      │   │               │   ├── order
      │   │               │   │   ├── controller
      │   │               │   │   ├── dao
      │   │               │   │   ├── entity
      │   │               │   │   └── service
      │   │               │   ├── user
      │   │               │   │   ├── controller
      │   │               │   │   ├── dao
      │   │               │   │   ├── entity
      │   │               │   │   └── service
            │   │               │   └── util
      │   │               │       └── JsonTOpagaData.java
      │   │               ├── scheduled
      │   │               │   ├── ScheduledMachine.java
      │   │               │   └── ScheduledProduct.java
      │   │               └── test
      │   │                   ├── Demo.java
      │   │                   └── Test.java
      │   └── resources
      │       ├── application-dev.yml
      │       ├── application-pro.yml
      │       ├── application-test.yml
      │       ├── application.yml
      │       └── mapper
      │           ├── goods
      │           │   └── GoodsDao.xml
      │           ├── machine
      │           │   └── MachineDao.xml
      │           ├── order
      │           └── user
└── test
    └── java
        └── com
            └── result
                └── demo
                    └── DemoApplicationTests.java

以下是几个核心的类

  • ExceptionResultAdvice :所有的异常都会通个这个类进行捕捉,处理,如果有新增加的Exception 只需要新增方法即可,如新增一个XXXException的方法
@ExceptionHandler(value = XXXException.class)
   public  Result XXXexceptionHandler(XXXException xxxexception){
       log.info("系统异常处理:{}",xxxexception);
       return Result.error();
   }
  • ResponseResultAdvice: 重写返回数据,这个类主要是对返回结果进行封装,而不需要在每一个Controller 里的方法进行封装,简化代码,做到只关注业务。

这里有一个坑:ResponseBodyAdvice 返回类型如果是String 的话会报错,原因大家可以自行查看,所以这个地方要对String 类型的数据进行处理

 @Override
    public Object beforeBodyWrite(Object body, MethodParameter methodParameter, MediaType mediaType, Class aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
        log.info("进入返回提重写格式 处理中。。。。。。");
        Result result = new Result();
        // 包含Result 说明是异常

        if (body instanceof Result) {
            return body;
        }
        if (body instanceof String) {
            String str = JSON.toJSONString(Result.success(body));
            return str;
        }
        return Result.success(body);
    }
  • ResponseResult: 这个是自定义的标签,主要是用来判断Controller 类的返回数据是否需要封装用法如下
@Slf4j
@RestController
@RequestMapping(value = "/api")
@ResponseResult
public class Test {
}

在ResponseResultInterceptor 类里会对这个标签进行判断处理。

public class ResponseResultInterceptor implements HandlerInterceptor {

    public static final String RESPONSE_RESULT_ANN = "RESPONSE-RESULT-ANN";

    /**
     * 此代码核心思想,就是获取此请求,是否需要返回值包装,设置一个属性标记。
     *
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {




        // 方法校验

        if (handler instanceof HandlerMethod) {
            final HandlerMethod handlerMethod = (HandlerMethod) handler;
            final Class<?> clazz = handlerMethod.getBeanType();
            final Method method = handlerMethod.getMethod();
            if (clazz.isAnnotationPresent(ResponseResult.class)) {
                request.setAttribute(RESPONSE_RESULT_ANN, clazz.getAnnotation(ResponseResult.class));
            } else if (method.isAnnotationPresent(ResponseResult.class)) {
                request.setAttribute(RESPONSE_RESULT_ANN, method.getAnnotation(ResponseResult.class));
            }
            return true;
        } else {
            return  initResponse(response, ResultCode.API_NOT_EXIST);
        }
    }


    private boolean initResponse(HttpServletResponse response,ResultCode resultCode) throws IOException {
        response.setCharacterEncoding("utf-8");
        response.setContentType("application/json; charset=utf-8");
        PrintWriter writer = response.getWriter();
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("code", resultCode.getCode());
        jsonObject.put("message", resultCode.getMessage());
        jsonObject.put("data", null);
        writer.write(jsonObject.toString());
        return false;
    }

小结:对于面向对象编程的逻辑思维要始终贯穿于整个项目,对象的封装也是必然,一个类是对象,多个类组成的模块也是对象,甚至在庞大的系统中,每一个子系统都是一个对象,这也就是OOP的核心 ,一切皆对象,所以没有对象的同学要抓紧时间找个对象,只有这样你才会知道如何面向对象!!!