后台服务最终的功能之一就是提供服务接口供其他方调用,减少不必要的混乱,那么定义统一的返回数据格式就显得尤为重要。
实现思路:
利用spring暴露的方法返回值处理接口类HandlerMethodReturnValueHandler,实现该接口类,定义标记有注解@ResponseBody的方法才需要处理,对方法返回的结果进行统一封装;
由于@ResponseBody的粒度比较大,有些可能不需要处理的接口也会被包含进去,所以自定义了注解@ApiResult,只有加了@ApiResult注解的类/方法才会真正被处理。
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;
}
}
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 {
}
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);
}
}
}
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 handlers=adapter.getReturnValueHandlers();
List 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);
}
}
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序列化