spring-boot统一包装返回体

简介: 使用拦截器和增强器设置返回体

版本

Spring-boot:2.3.1

方案

使用拦截器

常量类

定义一个统一的标识

public class Constant {
    public static final String RESPONSE_RESULT_ANNOTATION = "RESPONSE_RESULT_ANNOTATION";
}

返回体

import org.springframework.http.HttpStatus;

import java.io.Serializable;

/**
 * 接口返回对象
 */
public class Result<T> implements Serializable {
   private static final long serialVersionUID = 1L;
   private static Result<Object> result = new Result<>();

   /**
    * 失败消息
    */
   private String message;

   /**
    * 返回代码  
    */
   private Integer code;

   /**
    * 时间戳
    */
   private long timestamp = System.currentTimeMillis();

   /**
    * 结果对象
    */
   private T data;

   public Integer getCode() {
      return code;
   }

   public void setCode(Integer code) {
      this.code = code;
   }

   public long getTimestamp() {
      return timestamp;
   }

   public void setTimestamp(long timestamp) {
      this.timestamp = timestamp;
   }

   public String getMessage() {
      return message;
   }

   public void setMessage(String message) {
      this.message = message;
   }

   public T getData() {
      return data;
   }

   public void setData(T data) {
      this.data = data;
   }

   private Result() {}

   public static Result success() {
      Result result = new Result();
      result.setCode(HttpStatus.OK.value());
      return result;
   }

   public static Result<Object> success(Object data) {
      result.setCode(HttpStatus.OK.value());
      result.setData(data);
      return result;
   }

    public static Result<Object> success(Object data, String message) {
        result.setCode(HttpStatus.OK.value());
        result.setData(data);
        result.setMessage(message);
        return result;
    }

   public static Result<Object> failure(String message) {
      result.setCode(HttpStatus.INTERNAL_SERVER_ERROR.value());
      result.setMessage(message);
      return result;
   }

   public static Result<Object> failure(Integer code, String message) {
      result.setCode(code);
      result.setMessage(message);
      return result;
   }
}

注解类

import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@Documented
public @interface ResponseResult {
}

Retention指明可以通过反射读取
Target指明注解类可以注解到类,接口,枚举,方法
controller统一注解封装(可有可无)

import com.maintenance.response.ResponseResult;
import org.springframework.web.bind.annotation.RestController;

import java.lang.annotation.*;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@RestController
@ResponseResult
public @interface BaseControllerAnnotation {
}

此注解的作用就是在Controller中只需要写BaseControllerAnnotation就行了,不需要在写@RestController和@ResponseResult,所以只是用来简化代码的

配置类

import com.maintenance.response.ResponseResultInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebAppConfig implements WebMvcConfigurer {
    /**
     * 添加拦截器规则
     * @param registry
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry){
        registry.addInterceptor(new ResponseResultInterceptor()).addPathPatterns("/**");
    }
}

Configuration注解指明配置类,springboot自动扫描
addInterceptors中添加拦截器,可以添加多个,这里我们拦截了所有的请求

拦截器

import com.maintenance.pojo.BaseControllerAnnotation;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;

public class ResponseResultInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if(handler instanceof HandlerMethod) {
            /* 反射获取类和方法 */
            final HandlerMethod handlerMethod = (HandlerMethod) handler;
            final Class<?> clazz = handlerMethod.getBeanType();
            final Method method = handlerMethod.getMethod();
            /* 校验类的注解和方法的注解,满足条件后,添加到attribute */
            if(clazz.isAnnotationPresent(ResponseResult.class)) {
                request.setAttribute(Constant.RESPONSE_RESULT_ANNOTATION, clazz.getAnnotation(ResponseResult.class));
            } else if(method.isAnnotationPresent(ResponseResult.class)) {
                request.setAttribute(Constant.RESPONSE_RESULT_ANNOTATION, method.getAnnotation(ResponseResult.class));
            } else if(clazz.isAnnotationPresent(BaseControllerAnnotation.class)) {
                request.setAttribute(Constant.RESPONSE_RESULT_ANNOTATION, clazz.getAnnotation(BaseControllerAnnotation.class));
            }
        }
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
    }

    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
    }
}

增强器(处理返回)

import com.maintenance.excepion.RoleManagerException;
import com.maintenance.pojo.Result;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpStatus;
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.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import javax.servlet.http.HttpServletRequest;

@ControllerAdvice
public class ResponseResultHandler implements ResponseBodyAdvice<Object> {
    private Logger logger = LoggerFactory.getLogger(ResponseResultHandler.class);

    /**
     * 判断组件支持的类型
     * @param returnType
     * @param converterType
     * @return
     */
    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = servletRequestAttributes.getRequest();
        /* 判断是否需要统一包装,如果attribute中有,则表示需要处理,返回true */
        Object annotation = request.getAttribute(Constant.RESPONSE_RESULT_ANNOTATION);
        return !(annotation == null);
    }

    /**
     * 处理返回体
     * @param body
     * @param returnType
     * @param selectedContentType
     * @param selectedConverterType
     * @param request
     * @param response
     * @return
     */
    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        if(!(body instanceof Result)) {
            // 包装返回体
            return Result.success(body);
        }
        return body;
    }

    /**
     * 自定义异常
     * @param e
     * @return
     */
    @ExceptionHandler(RoleManagerException.class)
    @ResponseBody
    public Result<?> exceptionHandler(RoleManagerException e) {
        logger.error(e.getMessage(), e);
        return Result.failure(e.getMessage());
    }

    /**
     * 系统异常
     * @param e
     * @return
     */
    @ExceptionHandler(Exception.class)
    @ResponseBody
    public Result<?> exceptionHandler(Exception e) {
        logger.error(e.getMessage(), e);
        return Result.failure(HttpStatus.INTERNAL_SERVER_ERROR.value(), "系统异常,请联系管理员");
    }
}

使用方法

在controller类上使用@ResponseResult或@BaseControllerAnnotation
在controller方法上使用@ResponseResult

参考:

https://www.toutiao.com/i6694404645827117572

目录
相关文章
|
1月前
|
缓存 Java Maven
Java本地高性能缓存实践问题之SpringBoot中引入Caffeine作为缓存库的问题如何解决
Java本地高性能缓存实践问题之SpringBoot中引入Caffeine作为缓存库的问题如何解决
|
4月前
|
XML 安全 Java
深入实践springboot实战 蓄势待发 我不是雷锋 我是知识搬运工
springboot,说白了就是一个集合了功能的大类库,包括springMVC,spring,spring data,spring security等等,并且提供了很多和可以和其他常用框架,插件完美整合的接口(只能说是一些常用框架,基本在github上能排上名次的都有完美整合,但如果是自己写的一个框架就无法实现快速整合)。
|
2天前
|
Java 应用服务中间件 开发者
深入探索并实践Spring Boot框架
深入探索并实践Spring Boot框架
13 2
|
4月前
|
Java 数据安全/隐私保护
Neo4j【付诸实践 01】SpringBoot集成报错org.neo4j.driver.exceptions.ClientException:服务器不支持此驱动程序支持的任何协议版本(解决+源代码)
Neo4j【付诸实践 01】SpringBoot集成报错org.neo4j.driver.exceptions.ClientException:服务器不支持此驱动程序支持的任何协议版本(解决+源代码)
258 1
|
1月前
|
缓存 Java Spring
Java本地高性能缓存实践问题之在Spring Boot中启用缓存支持的问题如何解决
Java本地高性能缓存实践问题之在Spring Boot中启用缓存支持的问题如何解决
|
4月前
|
JavaScript Java 测试技术
返家乡”高校暑期社会实践微信小程序+springboot+vue.js附带文章和源代码设计说明文档ppt
返家乡”高校暑期社会实践微信小程序+springboot+vue.js附带文章和源代码设计说明文档ppt
31 0
|
4月前
|
Java API 时序数据库
InfluxData【付诸实践 02】SpringBoot 集成时序数据库 InfluxDB 应用分享(InfluxDB实例+Feign接口调用InfluxDB API)源码分享
InfluxData【付诸实践 02】SpringBoot 集成时序数据库 InfluxDB 应用分享(InfluxDB实例+Feign接口调用InfluxDB API)源码分享
147 0
|
JSON 前端开发 Java
【工作中问题解决实践 七】SpringBoot集成Jackson进行对象序列化和反序列化
【工作中问题解决实践 七】SpringBoot集成Jackson进行对象序列化和反序列化
388 0
|
弹性计算 Kubernetes 负载均衡
云原生之容器编排实践-以k8s的Service方式暴露SpringBoot服务
云原生之容器编排实践-以k8s的Service方式暴露SpringBoot服务
319 0
|
弹性计算 Kubernetes Cloud Native
云原生之容器编排实践-SpringBoot应用以Deployment方式部署到minikube以及弹性伸缩
云原生之容器编排实践-SpringBoot应用以Deployment方式部署到minikube以及弹性伸缩
236 0