新项目中不希望每次返回参数还要重新封装再返回,希望直接将查询数据或者返回数据统一定义进行返回,所以将新项目中的全局统一返回配置记录,备忘。同时分享给有需要的码友。(例:查询出来的对象集合直接返回,会统一封装在一个返回对象的data中丢给前端,保持返回数据统一)
1.创建统一返回对象:
import lombok.Data;
import java.io.Serializable;
/**
* 全局统一返回对象
*
* @author Tom
* @date 2020-12-14
*/
@Data
public class ResultMsg implements Serializable {
/**
* 返回状态(成功:true 失败:false)
*/
private boolean success;
/**
* 状态码(成功:200 失败:500)
*/
private Integer code;
/**
* 异常提示消息
*/
private String msg;
/**
* 返回参数
*/
private Object data;
public ResultMsg() {
}
public ResultMsg(boolean success) {
this.success = success;
}
public ResultMsg(boolean success, Integer code) {
this.success = success;
this.code = code;
}
public ResultMsg(boolean success, Integer code, String msg) {
this.success = success;
this.code = code;
this.msg = msg;
}
public ResultMsg(boolean success, Integer code, String msg, Object data) {
this.success = success;
this.code = code;
this.msg = msg;
this.data = data;
}
public ResultMsg(boolean success, Integer code, Object data) {
this.success = success;
this.code = code;
this.data = data;
}
}
2.创建全局统一返回启用注解(作用于启动类上)
import com.xiaomi.upupoo.config.GlobalDefaultConfig;
import org.springframework.context.annotation.Import;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 全局统一返回启用注解(作用于启动类上)
*
* @author Tom
* @date 2020-12-18
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Import(GlobalDefaultConfig.class)
public @interface EnableGlobalResponse {}
3.创建全局统一返回忽略标识注解(作用于: 类或方法上,标识后的类或方法则不进行全局返回的封装)
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 全局统一返回忽略标识注解(作用于: 类或方法上,标识后的类或方法则不进行全局返回的封装)
*
* @author Tom
* @date 2020-12-18
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface IgnoreGlobalResponse {}
4.创建需要忽略的配置文件dispose.properties
# 全局统一返回忽略配置
# 过滤 swagger(若项目中使用)
dispose.advice-filter-package[0]=springfox.documentation
# 过滤 spring 官方包
dispose.advice-filter-package[1]=org.springframework
5.定义默认过滤配置中的包和类
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.util.ArrayList;
import java.util.List;
/**
* 定义默认过滤配置中的包和类
*
* @author Tom
* @date 2020-12-18
*/
@ConfigurationProperties(GlobalDefaultProperties.PREFIX)
public class GlobalDefaultProperties {
/**
* 定义过滤拦截头部
*/
public static final String PREFIX = "dispose";
/**
* 统一返回过滤包
*/
private List<String> adviceFilterPackage = new ArrayList<>();
/**
* 统一返回过滤类
*/
private List<String> adviceFilterClass = new ArrayList<>();
public List<String> getAdviceFilterPackage() {
return adviceFilterPackage;
}
public void setAdviceFilterPackage(List<String> adviceFilterPackage) {
this.adviceFilterPackage = adviceFilterPackage;
}
public List<String> getAdviceFilterClass() {
return adviceFilterClass;
}
public void setAdviceFilterClass(List<String> adviceFilterClass) {
this.adviceFilterClass = adviceFilterClass;
}
}
6.全局统一返回处理类(重要)
import com.alibaba.fastjson.JSON;
import com.xiaomi.upupoo.common.annotation.response.IgnoreGlobalResponse;
import com.xiaomi.upupoo.common.pojo.vo.ResultMsg;
import com.xiaomi.upupoo.config.GlobalDefaultProperties;
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.lang.Nullable;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
/**
* 全局统一返回处理类(重要)
* {@link IgnoreGlobalResponse} 处理解析 {@link ResponseBodyAdvice} 统一返回包装器
*
* @author Tom
* @date 2020-12-18
*/
@RestControllerAdvice
public class GlobalResponseHandler implements ResponseBodyAdvice<Object> {
private GlobalDefaultProperties globalDefaultProperties;
/**
* 通过构造器将默认过滤配置注入
* @param globalDefaultProperties
*/
public GlobalResponseHandler(GlobalDefaultProperties globalDefaultProperties) {
this.globalDefaultProperties = globalDefaultProperties;
}
/**
* 重写supports方法,进行自定义规则拦截
* @param methodParameter
* @param aClass
* @return
*/
@Override
public boolean supports(MethodParameter methodParameter,Class<? extends HttpMessageConverter<?>> aClass) {
return filter(methodParameter);
}
/**
* 全局统一返回处理类 - 核心方法
* 返回值为Object类型并且返回为空 AbstractMessageConverterMethodProcessor#writeWithMessageConverters 方法
* 无法触发调用本类的 beforeBodyWrite 处理,所以在Controller层尽量避免直接使用 "Object" 类型返回。
* @param o
* @param methodParameter
* @param mediaType
* @param aClass
* @param serverHttpRequest
* @param serverHttpResponse
* @return
*/
@Nullable
@Override
public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType,Class<? extends HttpMessageConverter<?>> aClass,
ServerHttpRequest serverHttpRequest,ServerHttpResponse serverHttpResponse) {
/**o is null -> return response*/
if (o == null) {
/**当 o 返回类型为 string 并且为null会出现 java.lang.ClassCastException: Result cannot be cast to java.lang.String,则封装ResultMsg对象并转换为String返回*/
if (methodParameter.getParameterType().getName().equals("java.lang.String")) {
return JSON.toJSON(new ResultMsg(true,200)).toString();
}
return new ResultMsg(true,200);
}
/**当 o 返回类型为ResultMsg(统一封装返回对象),则直接返回*/
if (o instanceof ResultMsg) {
return o;
}
/**当 o 为string 则特殊处理 java.lang.ClassCastException: Result cannot be cast to java.lang.String,封装ResultMsg对象并转换为String返回*/
if (o instanceof String) {
return JSON.toJSON(new ResultMsg(true,200,o)).toString();
}
return new ResultMsg(true,200,o);
}
/**
* 自定义规则拦截器
* 过滤: 1.检查过滤包路径 2.检查<类>过滤列表 3.检查忽略注解是否存在于类上 4.检查注解是否存在于方法上
* @param methodParameter
* @return
*/
private Boolean filter(MethodParameter methodParameter) {
Class<?> declaringClass = methodParameter.getDeclaringClass();
/**检查过滤包路径*/
long count = globalDefaultProperties.getAdviceFilterPackage().stream().filter(l -> declaringClass.getName().contains(l)).count();
if (count > 0) {
return false;
}
/**检查<类>过滤列表*/
if (globalDefaultProperties.getAdviceFilterClass().contains(declaringClass.getName())) {
return false;
}
/**检查忽略注解是否存在于类上*/
if (methodParameter.getDeclaringClass().isAnnotationPresent(IgnoreGlobalResponse.class)) {
return false;
}
/**检查注解是否存在于方法上*/
if (methodParameter.getMethod().isAnnotationPresent(IgnoreGlobalResponse.class)) {
return false;
}
return true;
}
}
7.将全局统一返回注入spring容器(将需要忽略的包或类从配置中获取)
import com.xiaomi.upupoo.common.GlobalResponseHandler;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
/**
* 全局统一返回注入spring容器(将需要忽略的包或类从配置中获取)
*
* @author Tom
* @date 2020-12-18
*/
@Configuration
@EnableConfigurationProperties(GlobalDefaultProperties.class)
@PropertySource(value = "classpath:config/dispose.properties", encoding = "UTF-8")
public class GlobalDefaultConfig {
@Bean
public GlobalResponseHandler commonResponseDataAdvice(GlobalDefaultProperties globalDefaultProperties) {
return new GlobalResponseHandler(globalDefaultProperties);
}
}
8.完成配置,将@EnableGlobalResponse注解加入启动类上即可。
9.实例显示:
/**
* 数据字典 - 查询所有类型
* @return
*/
@ResponseBody
@GetMapping(value = "/queryAll")
public List<DictionaryVo> queryAll() {
return dictionaryService.queryAll();
}
返回的List集合对象被统一处理,返回给前端一个ResultMsg Json对象,数据则被封装在data参数中。