SpringBoot - 利用自定义注解@ResponseResult和ResponseBodyAdvice接口进行统一响应处理

1. 背景说明

这里类似于统一异常处理,不熟悉的话,可以学习下。在做前后端分离的项目中,我们想要统一每个服务返回给前端的数据格式,比如所有的接口都以下面这种数据格式响应给前端:

{
    "code": 0,
    "message": "succeed",
    "data": {
        
    }
}

2. 封装响应数据类 ApiResponse

@Data
public class ApiResponse<T> {

    public static final int CODE_OK = 0;

    public static final int CODE_ERR_COMMON = -1;

    public static final int CODE_PARAMETER_VALIDATE_FAILED = 1;

    public static final String MSG_SUCCEED = "succeed";

    private static final ApiResponse<Object> OK = new ApiResponse<>(CODE_OK, MSG_SUCCEED);

    private int code;
    private String message;

    @JsonInclude(JsonInclude.Include.NON_NULL)
    private T data;

    public ApiResponse() { }

    public ApiResponse(int code, String message) {
        this.code = code;
        this.message = message;
    }

    public ApiResponse(int code, String message, T data) {
        this.code = code;
        this.message = message;
        this.data = data;
    }

    public static ApiResponse<Object> ok() {
        return OK;
    }

    public static ApiResponse<Object> newInstance(int code, String message) {
        return new ApiResponse<>(code, message);
    }

    public static <E> ApiResponse<E> newInstance(int code, String message, E data) {
        return new ApiResponse<>(code, message, data);
    }

    public static <E> ApiResponse<E> newInstance(E data) {
        try {
            return new ApiResponse<>(CODE_OK,"success", data);
        } catch (Exception e) {
            return new ApiResponse<>(CODE_OK, MSG_SUCCEED, data);
        }
    }
}

3. 自定义注解 @ResponseResult

/**
 * 此注解用于返回值包装,将返回值包装成{@link com.hh.vo.ApiResponse}对象
 */
@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ResponseResult {
    
}

4. 自定义 ResponseAdvice 实现 ResponseBodyAdvice 接口

实现ResponseBodyAdvice接口,其实是对加了@RestController(也就是@Controller+@ResponseBody)注解的处理器将要返回的值进行增强处理。此接口对添加了@Controller和@ResponseBody的类织入一个通知 (增强功能)

/**
 * 返回值统一处理类
 */
@Component
@Slf4j
// Controller层加了@ResponseResult注解的所有响应对象都会经过此拦截器,并对响应体进行封装
@RestControllerAdvice(annotations = ResponseResult.class) 
public class ResponseAdvice implements ResponseBodyAdvice<Object> {
    /**
     * 通过该方法可以选择哪些类或那些方法的response要进行处理, 其他的不进行处理.
     *
     * @param returnType 方法返回的类型
     * @param converterType 参数类型装换
     * @return true,则下面 beforeBodyWrite方法被调用, 否则就不调用下述方法
     */
    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        // 方法的返回类型
        Class<?> type = returnType.getParameterType();
        log.debug("return type is :{}", type);
        return  !ApiResponse.class.isAssignableFrom(type) 
            	&& !ModelAndView.class.isAssignableFrom(type)
                && !CharSequence.class.isAssignableFrom(type) 
                && !ResponseEntity.class.isAssignableFrom(type);
    }

    /**
     * 对response方法进行具体操作处理
     *
     * @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
    ) {
        log.debug("going to convert response body to ApiResponse");
        // 封装返回给前端的响应体
        return ApiResponse.newInstance(body);
    }
}

ResponseBodyAdvice 接口源码:

/**
 * Allows customizing the response after the execution of an {@code @ResponseBody}
 * or a {@code ResponseEntity} controller method but before the body is written
 * with an {@code HttpMessageConverter}.
 */
public interface ResponseBodyAdvice<T> {

   boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType);

   @Nullable
   T beforeBodyWrite(@Nullable T body, MethodParameter returnType, MediaType selectedContentType,
         Class<? extends HttpMessageConverter<?>> selectedConverterType,
         ServerHttpRequest request, ServerHttpResponse response);
}

5. 使用自定义注解 @ResponseResult

@Data
public class UserEntity {

    private Integer id;

    private String name;

    private String password;

    private Date createTime;
}
@ResponseResult
@RestController
public class UserController {

    @Autowired
    private UserService userService;

    @PostMapping("/add")
    public UserEntity add(@RequestBody UserEntity userEntity){
        // 可以看到并没有对响体 UserEntity 按照前后端交互的数据响应格式进行封装
        return userService.insertUser(userEntity);
    }
}
@Service
public class UserService {

    @Autowired
    private UserDao userDao;

    @CheckParam
    public UserEntity  insertUser(UserEntity userEntity){
        int insert = userDao.insert(userEntity);
        return userEntity;
    }
}

可以看到,只要给Controller层控制器加上@ResponseResult注解,就可以对响应数据格式进行统一处理:

在这里插入图片描述

  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我一直在流浪

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值