在上一家公司中我经常看到接口中返回的直接是一个对象集。然而在响应后却又封装了{msg:'',state:'',obj:''},这些参数我就很纳闷这个是如何实现的。我第一反应就是使用了AOP。但是找了许久并未发现哪里使用了AOP的形式。经过层层的深入我发现了一个接口和一个注解,才慢慢的打开迷层。
ResponseBodyAdvice
ResponseBodyAdvice 这个接口一看就是通过增强器进行织入的,我们从Advice就可以看出。这个需要配合@ControllerAdvice或者@RestControllerAdvice
ResponseBodyAdvice接口是spring4.1的特性,其作用是在响应体写出前做一些处理,比如修改返回值,加密等。允许在执行@ResponseBody或ResponseEntity控制器方法之后但在使用HttpMessageConverter编写正文之前自定义响应。可以直接在RequestMappingHandlerAdapter和ExceptionHandlerExceptionResolver注册实现,或者更有可能在@ControllerAdvice中注解,在这种情况下,它们都会被两者自动检测到。
下面我们来看下这个接口的定义
public interface ResponseBodyAdvice<T> {
/**
* 此组件是否支持给定的控制器方法返回类型和所选的HttpMessageConverter类型。
* 这个方法返回true后才会执行下面的beforeBodyWrite方法
*/
boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType);
/**
* 在选择HttpMessageConverter之后且在调用其write方法之前调用
*/
@Nullable
T beforeBodyWrite(@Nullable T body, MethodParameter returnType, MediaType selectedContentType,
Class<? extends HttpMessageConverter<?>> selectedConverterType,
ServerHttpRequest request, ServerHttpResponse response);
}
接口的使用我们直接在Controller进行增强,这里我们增强的是RestController看代码
@RestControllerAdvice(basePackages = "com.kaysanshi")
public class ResponseControllerAdvice implements ResponseBodyAdvice<Object> {
@Override
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
// 获取当前controller请求的方法是否有SkipR注解
SkipR skipR = returnType.getMethod().getAnnotation(SkipR.class);
if(null != skipR)
return false;
// 是否是返回的R对象
return !returnType.getMethod().getReturnType().equals(R.class);
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request,
ServerHttpResponse response) {
// 这个地方是自己判断的其他的直接返回当前对象,不进行包装
if(request.getHeaders().containsKey(SecurityConstants.INNER) && SecurityConstants.INNER_TRUE.equalsIgnoreCase(request.getHeaders().getFirst(SecurityConstants.INNER))) {
return body;
}
if (returnType.getGenericParameterType().equals(String.class)) {
return JSON.toJSONString(new R<>(body));
}
return new R<>(body);
}
}
在这个beforeBodyWrite的方法中我们可以实现自己的想要的内容,这里有我们通过inner注解进行过滤了fegin中返回的封装,直接将响应返回到上游。当不是标示inner的调用,我们将其封装到返回实体的对象中。
在看这个接口我发现FastJson也是通过对这个接口的实现,封装了一下将对象转为Json的使用。而我们不使用Rest处理的时候,就是
FastJsonViewResponseBodyAdvice 实现了对ResponseBodyAdvice的接口的实现
对应的返回实体R对象的封装如下
@Builder
@Accessors(chain = true)
@AllArgsConstructor
@NoArgsConstructor
@Data
public class R<T> implements Serializable {
private static final long serialVersionUID = -9141307870732228707L;
private String state;
private String msg;
private T obj;
public R(T obj){
super();
this.state = CommonConstants.SUCCESS;
this.obj = obj;
}
public R(FirstException e) {
super();
this.state = e.getCode();
this.msg = e.getMessage();
}
public R(FirstArgsException e) {
super();
this.state = e.getCode();
this.msg = e.getMessage();
}
public R(LogicException e) {
super();
this.state = e.getCode();
this.msg = e.getMessage();
}
public R(LogicArgsException e) {
super();
this.state = e.getCode();
this.msg = e.getMessage();
}
public R(AccessDeniedException e) {
super();
this.state = SecurityConstants.ACCESS_DENIED;
this.msg = e.getMessage();
}
public R(NotBreakerException e) {
super();
this.state = e.getCode();
this.msg = e.getMessage();
}
public R(Throwable e) {
super();
if(InfoUtils.isContain(e.getMessage())) {
this.state = e.getMessage();
this.msg = InfoUtils.getInfo(this.state);
} else {
this.state = CommonConstants.FAIL;
this.msg = InfoUtils.getInfo(FirstException.ERROR_CODE);
}
}
public R<T> success(T obj){
this.state = CommonConstants.SUCCESS;
this.obj = obj;
return this;
}
public R<T> error(String msg){
this.state = CommonConstants.FAIL;
this.msg = msg;
return this;
}
public R<T> error(){
this.state = CommonConstants.FAIL;
this.msg = InfoUtils.getInfo(FirstException.ERROR_CODE);
return this;
}
@JSONField(serialize = false)
@JsonIgnore
public Boolean getIsSuccess() {
return Objects.equal(CommonConstants.SUCCESS, this.state);
}
public String toJson() {
return JSON.toJSONString(this);
}
}
经过以上的方式我们就可以做成一个全局的统一响应的封装。
ControllerAdvice
这样我们已经知道ControllerAdvice的一个应用场景,是结合ResponseBodyAdvice进行使用的,同样我们也可以将ControllerAdvice的应用到其他场景。比如:统一异常处理,全局数据绑定,全局数据处理(上面的可以算成这个场景)。
/**
* user:kay三石
* time: 8:44
* desc: 公共的异常处理类
**/
@ControllerAdvice
public class BaseExceptionHandler {
@ExceptionHandler(Exception.class)
@ResponseBody
public BaseResult error(Exception e) {
e.printStackTrace();
System.out.println("调用了公共异常处理类");
return BaseResult.notOk(e.getMessage());
}
}
当然我们也可以通过这两个结合做一些其他方面的实现,其他的可以自行去查阅资料进行使用。这里我们主要是说的ResponseBodyAdvice和@ControllerAdvice的一起使用返回统一的响应格式。
本文暂时没有评论,来添加一个吧(●'◡'●)