专业的编程技术博客社区

网站首页 > 博客文章 正文

统一响应接口封装(统一响应接口封装图)

baijin 2024-08-20 10:13:55 博客文章 12 ℃ 0 评论

在上一家公司中我经常看到接口中返回的直接是一个对象集。然而在响应后却又封装了{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的一起使用返回统一的响应格式。

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表