spring-boot学习:十二、spring-boot统一返回数据格式

后台服务最终的功能之一就是提供服务接口供其他方调用,减少不必要的混乱,那么定义统一的返回数据格式就显得尤为重要。

实现思路:
利用spring暴露的方法返回值处理接口类HandlerMethodReturnValueHandler,实现该接口类,定义标记有注解@ResponseBody的方法才需要处理,对方法返回的结果进行统一封装;
由于@ResponseBody的粒度比较大,有些可能不需要处理的接口也会被包含进去,所以自定义了注解@ApiResult,只有加了@ApiResult注解的类/方法才会真正被处理。

  1. 定义统一的结果类ApiResultEntity.java
package com.kevin.core.result;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.kevin.core.page.PageLimit;
import lombok.Getter;
import lombok.Setter;

import java.io.Serializable;

/**
 * 统一返回对象
 * @author Kevin
 */
@Setter
@Getter
public class ApiResultEntity implements Serializable {

	// 正常
	public final static Integer RESULT_SUCCESS = 200;

	// 系统异常
	public final static Integer RESULT_EXCEPTION = 500;

	/**
	 * 请求结果状态位
	 */
	private Integer code = ApiResultEntity.RESULT_SUCCESS;

	/**
	 * 警告或者异常信息,正常无信息
	 */
	private String text;

	/**
	 * 正常结束返回值
	 */
	private Object data;

	/**
	 * 分页信息
	 */
	@JsonInclude(JsonInclude.Include.NON_NULL)
	private PageLimit pageLimit;

	// ------------------------------构造函数------------------------------//
	public ApiResultEntity() {
	}

	public ApiResultEntity(Integer code) {
		this.code = code;
	}

	public ApiResultEntity(Object data) {
		this.data = data;
	}

	public ApiResultEntity(Integer code, String text) {
		this.code = code;
		this.text = text;
	}

	public ApiResultEntity(Integer code, String text, Object data) {
		this.code = code;
		this.text = text;
		this.data = data;
	}

	public ApiResultEntity(Object data, PageLimit pageLimit) {
		this.data = data;
		this.pageLimit = pageLimit;
	}

	public ApiResultEntity(Integer code, Object data, PageLimit pageLimit) {
		this.code = code;
		this.data = data;
		this.pageLimit = pageLimit;
	}

	public ApiResultEntity(Integer code, String text, Object data, PageLimit pageLimit) {
		this.code = code;
		this.text = text;
		this.data = data;
		this.pageLimit = pageLimit;
	}
}
  1. 定义需要处理返回的结果的注解@ApiResult
package com.kevin.core.result;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @author kevin
 */
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiResult {

}
  1. 编写结果处理类ApiResultHandler,实现spring提供的HandlerMethodReturnValueHandler接口,对需要处理的接口进行处理;
package com.kevin.core.result;

import com.github.pagehelper.Page;
import com.kevin.core.page.PageLimit;
import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.method.support.ModelAndViewContainer;

/**
 * 请求统一返回数据格式化
 * @author kevin
 *
 */
public class ApiResultHandler implements HandlerMethodReturnValueHandler{

	private final HandlerMethodReturnValueHandler delegate;
	
	public ApiResultHandler(HandlerMethodReturnValueHandler delegate) {
		this.delegate = delegate;
	}
	
	/**
	 * 判断是否满足处理条件
	 * 有注解@ResponseBody标记的方法返回进行处理(返回json)
	 */
	@Override
	public boolean supportsReturnType(MethodParameter returnType) {
		
		return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) ||
					returnType.hasMethodAnnotation(ResponseBody.class));
	}

	/**
	 * 如果类或者方法含@ApiResult注解,则对对响应结果进行包装,然后交给默认的RequestResponseBodyMethodProcessor继续处理
	 * 否则保持默认方式处理
	 */
	@Override
	public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer,
			NativeWebRequest webRequest) throws Exception {
		if(AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ApiResult.class) ||
				returnType.hasMethodAnnotation(ApiResult.class)){
			PageLimit pageLimit = null;
			if(returnValue instanceof Page){
				Page page = (Page) returnValue;
				pageLimit = new PageLimit(page.getPageNum(), page.getPageSize(), page.getTotal());
			}

			delegate.handleReturnValue(new ApiResultEntity(returnValue, pageLimit), returnType, mavContainer, webRequest);
		}else{
			delegate.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
		}
	}

}
  1. 编写配置类ApiResultConfig.java,将ApiResultHandler注册到spring中,也就是替换spring自带的RequestResponseBodyMethodProcessor
package com.kevin.config;

import com.kevin.core.result.ApiResultHandler;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
import org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor;

import java.util.ArrayList;
import java.util.List;

/**
 * 注册结果处理类
 * @author Kevin Yu
 *
 */
@Configuration
public class ApiResultConfig implements InitializingBean{

	@Autowired
    private RequestMappingHandlerAdapter adapter;
	
	@Override
	public void afterPropertiesSet() throws Exception {
		List<HandlerMethodReturnValueHandler> handlers=adapter.getReturnValueHandlers();
		List<HandlerMethodReturnValueHandler> newHandlers=new ArrayList<>(handlers.size());
		
		for (HandlerMethodReturnValueHandler handler : handlers) {
			if(handler instanceof RequestResponseBodyMethodProcessor) {
				newHandlers.add(new ApiResultHandler(handler));
			}else {
				newHandlers.add(handler);
			}
		}
		
		adapter.setReturnValueHandlers(newHandlers);
	}

}
  1. 测试
package com.kevin.fish.controller.demo;

import com.kevin.core.result.ApiResult;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author kevin
 * @create 2020/8/7 14:42
 */
@ApiResult
@RestController
public class TestController {

    @GetMapping(value="/test",produces= MediaType.APPLICATION_JSON_VALUE)
    public Object test(){
        return "Hello Kevin";
    }
}

调用接口返回结果:
{“code”:200,“text”:null,“data”:“Hello Kevin”}

返回的结果中没有pageLimit的原因是加了注解@JsonInclude(JsonInclude.Include.NON_NULL),有值才会被JSON序列化

猜你喜欢

转载自blog.csdn.net/guangcaiwudong/article/details/107861461