前言

最近在优化自己之前基于Spring AOP的统一响应体的实现方案。

什么是统一响应体呢?在目前的前后端分离架构下,后端主要是一个RESTful API的数据接口。

但是HTTP的状态码数量有限,而随着业务的增长,HTTP状态码无法很好地表示业务中遇到的异常情况。

那么可以通过修改响应返回的JSON数据,让其带上一些固有的字段,例如以下这样的

{

"code": 10000,

"msg": "success",

"data": {

"id": 2,

"name": "test"

}

}

其中关键属性的用途如下:

code为返回结果的状态码

msg为返回结果的消息

data为返回的业务数据

这3个属性为固有属性,每次响应结果都会有带有它们。

需求

希望实现一个能够代替基于AOP的实现方案,需要满足以下几点:

原有的基于AOP的实现方案需要Controller的返回类型为Object,需要新方案不限制返回类型

原有的基于AOP的实现方案需要通过切面表达式+注解控制切点的Controller(注解的包名修改会导致切面表达式的修改,即需要修改两处地方),需要新方案能够基于注解,而不需要修改切面表达式

方案思路

基于上述的需求,选择使用Spring的Controller增强机制,其中关键的类为以下3个:

@ControllerAdvice:类注解,用于指定Controller增强处理器类。

ResponseBodyAdvice:接口,实现后beforeBodyWrite()方法后可以对响应的body进行修改,需要结合@ControllerAdvice使用。

@ExceptionHandler:方法注解,用于指定异常处理方法,需要结合@ControllerAdvice和@ResponseBody使用。

示例关键代码

本示例使用的Spring Boot版本为2.1.6.RELEASE,同时需要开发工具安装lombok插件

引入依赖

org.springframework.boot

spring-boot-starter-web

org.projectlombok

lombok

true

org.springframework.boot

spring-boot-starter-test

test

统一响应体

Controller增强后统一响应体对应的对象

import lombok.AllArgsConstructor;

import lombok.Data;

import java.io.Serializable;

/**

* 统一的公共响应体

* @author NULL

* @date 2019-07-16

*/

@Data

@AllArgsConstructor

public class ResponseResult implements Serializable {

/**

* 返回状态码

*/

private Integer code;

/**

* 返回信息

*/

private String msg;

/**

* 数据

*/

private Object data;

}

统一响应注解

统一响应注解是一个标记是否开启统一响应增强的注解

import java.lang.annotation.ElementType;

import java.lang.annotation.Retention;

import java.lang.annotation.RetentionPolicy;

import java.lang.annotation.Target;

/**

* 统一响应注解

* 添加注解后,统一响应体才能生效

* @author NULL

* @date 2019-07-16

*/

@Retention(RetentionPolicy.RUNTIME)

@Target({ElementType.METHOD, ElementType.TYPE})

public @interface BaseResponse {

}

状态码枚举

统一响应体中返回的状态码code和状态信息msg对应的枚举类

/**

* 返回状态码

*

* @author NULL

* @date 2019-07-16

*/

public enum ResponseCode {

/**

* 成功返回的状态码

*/

SUCCESS(10000, "success"),

/**

* 资源不存在的状态码

*/

RESOURCES_NOT_EXIST(10001, "资源不存在"),

/**

* 所有无法识别的异常默认的返回状态码

*/

SERVICE_ERROR(50000, "服务器异常");

/**

* 状态码

*/

private int code;

/**

* 返回信息

*/

private String msg;

ResponseCode(int code, String msg) {

this.code = code;

this.msg = msg;

}

public int getCode() {

return code;

}

public String getMsg() {

return msg;

}

}

业务异常类

业务异常类是用于识别业务相关的异常,需要注意这个异常类强制需要以ResponseCode作为构造方法入参,这样可以通过捕获异常获得返回的状态码信息

import com.rjh.web.response.ResponseCode;

import lombok.Data;

import lombok.EqualsAndHashCode;

/**

* 业务异常类,继承运行时异常,确保事务正常回滚

*

* @author NULL

* @since 2019-07-16

*/

@Data

@EqualsAndHashCode(callSuper = false)

public class BaseException extends RuntimeException{

private ResponseCode code;

public BaseException(ResponseCode code) {

this.code = code;

}

public BaseException(Throwable cause, ResponseCode code) {

super(cause);

this.code = code;

}

}

异常处理类

用于处理Controller运行时未捕获的异常的处理类。

import com.rjh.web.exception.BaseException;

import lombok.extern.slf4j.Slf4j;

import org.springframework.web.bind.annotation.ControllerAdvice;

import org.springframework.web.bind.annotation.ExceptionHandler;

import org.springframework.web.bind.annotation.ResponseBody;

/**

* 异常处理器

*

* @author NULL

* @since 2019-07-16

*/

@ControllerAdvice(annotations = BaseResponse.class)

@ResponseBody

@Slf4j

public class ExceptionHandlerAdvice {

/**

* 处理未捕获的Exception

* @param e 异常

* @return 统一响应体

*/

@ExceptionHandler(Exception.class)

public ResponseResult handleException(Exception e){

log.error(e.getMessage(),e);

return new ResponseResult(ResponseCode.SERVICE_ERROR.getCode(),ResponseCode.SERVICE_ERROR.getMsg(),null);

}

/**

* 处理未捕获的RuntimeException

* @param e 运行时异常

* @return 统一响应体

*/

@ExceptionHandler(RuntimeException.class)

public ResponseResult handleRuntimeException(RuntimeException e){

log.error(e.getMessage(),e);

return new ResponseResult(ResponseCode.SERVICE_ERROR.getCode(),ResponseCode.SERVICE_ERROR.getMsg(),null);

}

/**

* 处理业务异常BaseException

* @param e 业务异常

* @return 统一响应体

*/

@ExceptionHandler(BaseException.class)

public ResponseResult handleBaseException(BaseException e){

log.error(e.getMessage(),e);

ResponseCode code=e.getCode();

return new ResponseResult(code.getCode(),code.getMsg(),null);

}

}

响应增强类

Conrtoller增强的统一响应体处理类,需要注意异常处理类已经进行了增强,所以需要判断一下返回的对象是否为统一响应体对象。

import lombok.extern.slf4j.Slf4j;

import org.springframework.core.MethodParameter;

import org.springframework.http.MediaType;

import org.springframework.http.server.ServerHttpRequest;

import org.springframework.http.server.ServerHttpResponse;

import org.springframework.web.bind.annotation.ControllerAdvice;

import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

/**

* 统一响应体处理器

* @author NULL

* @date 2019-07-16

*/

@ControllerAdvice(annotations = BaseResponse.class)

@Slf4j

public class ResponseResultHandlerAdvice implements ResponseBodyAdvice {

@Override

public boolean supports(MethodParameter returnType, Class converterType) {

log.info("returnType:"+returnType);

log.info("converterType:"+converterType);

return true;

}

@Override

public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {

if(MediaType.APPLICATION_JSON.equals(selectedContentType) || MediaType.APPLICATION_JSON_UTF8.equals(selectedContentType)){ // 判断响应的Content-Type为JSON格式的body

if(body instanceof ResponseResult){ // 如果响应返回的对象为统一响应体,则直接返回body

return body;

}else{

// 只有正常返回的结果才会进入这个判断流程,所以返回正常成功的状态码

ResponseResult responseResult =new ResponseResult(ResponseCode.SUCCESS.getCode(),ResponseCode.SUCCESS.getMsg(),body);

return responseResult;

}

}

// 非JSON格式body直接返回即可

return body;

}

}

使用示例

首先准备一个User对象

import lombok.Data;

import lombok.EqualsAndHashCode;

import java.io.Serializable;

/**

* 用户类

* @author NULL

* @date 2019-07-16

*/

@Data

@EqualsAndHashCode

public class User implements Serializable {

private Integer id;

private String name;

}

然后是准备一个简单的UserController即可

import com.rjh.web.entity.User;

import com.rjh.web.exception.BaseException;

import com.rjh.web.response.BaseResponse;

import com.rjh.web.response.ResponseCode;

import org.springframework.web.bind.annotation.GetMapping;

import org.springframework.web.bind.annotation.PathVariable;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RestController;

/**

* 测试用的Controller

*

* @author NULL

* @date 2019-07-16

*/

@BaseResponse

@RestController

@RequestMapping("users")

public class UserController {

@GetMapping("/{userId}")

public User getUserById(@PathVariable Integer userId){

if(userId.equals(0)){

throw new BaseException(ResponseCode.RESOURCES_NOT_EXIST);

}

if(userId.equals(1)){

throw new RuntimeException();

}

User user=new User();

user.setId(userId);

user.setName("test");

return user;

}

}

运行结果

在浏览器直接访问http://127.0.0.1:8080/users/0,则返回结果如下(结果经过格式化处理):

{

"code": 10001,

"msg": "资源不存在",

"data": null

}

在浏览器直接访问http://127.0.0.1:8080/users/1,则返回结果如下(结果经过格式化处理):

{

"code": 50000,

"msg": "服务器异常",

"data": null

}

在浏览器直接访问http://127.0.0.1:8080/users/2,则返回结果如下(结果经过格式化处理):

{

"code": 10000,

"msg": "success",

"data": {

"id": 2,

"name": "test"

}

}

由运行结果可以得知统一响应增强其实已经生效了,而且能够很好的处理异常。

示例代码地址

下面是这个示例的代码地址,如果觉得不错或者帮助到你,希望大家给个Star:

https://github.com/spring-bas...

参考资料

baseresponse响应类_SpringBoot统一响应体解决方案相关推荐

  1. baseresponse响应类_Java response响应体和文件下载实现原理

    通过response 设置响应体: 响应体设置文本: PrintWriter getWriter() 获得字符流,通过字符流的write(String s)方法可以将字符串设置到response 缓冲 ...

  2. 基于Spring AOP的统一响应体的实现(注解版)

    基于Spring AOP的统一响应体的实现(注解版) 一.前言 在上一篇系列中 我们 统一参数校验,统一结果响应,统一异常处理,统一错误处理,统一日志记录,统一生成api文档, 对于统一数据响应返回规 ...

  3. spring boot / cloud (二) 规范响应格式以及统一异常处理

    spring boot / cloud (二) 规范响应格式以及统一异常处理 前言 为什么规范响应格式? 我认为,采用预先约定好的数据格式,将返回数据(无论是正常的还是异常的)规范起来,有助于提高团队 ...

  4. 2022-05-02 一.统一响应码

    统一响应码 前言 统一响应码 响应状态 响应结果 例子 前言 作为后端服务器,返回给前端的数据需要统一格式,一般为: {code:200,msg: "接口请求成功",data:.. ...

  5. baseresponse响应类_内部类、响应类Response、序列化基类、反序列化、全局局部钩子...

    一.内部类 1.概念:将类定义在一个类的内部,被定义的类就是内部类 2.特点:内部类及内部类的所以名称空间,可以直接被外部类访问的 3. 应用:通过内部类的名称空间,给外部类额外拓展一些特殊的属性(配 ...

  6. Servlet基础:接口、类、请求响应、配置、会话追踪、上下文、协作、异常

    10.1 Servlet介绍 ​ Servlet技术是Sun公司提供的一种实现动态网页的解决方案,它是基于Java编程语言的Web服务器端编程技术,主要用于在Web服务器端获得客户端的访问请求信息和动 ...

  7. SpringCloud采用Jackson序列化统一响应不正当的消息转换器导致的异常问题

    SpringCloud采用Jackson序列化统一响应不正当的消息转换器导致的异常问题 环境说明 org.springframework.cloud.spring-cloud-dependencies ...

  8. springMVC02-SSM整合(Result统一响应数据格式、异常页面修改、SSM整合vue-elementUI小案例、SpringMVC的拦截器Interceptor)

    文章目录 今日内容 一.SSM整合[重点] 1 SSM整合配置 问题导入 1.1 SSM整合流程 1.2 SSM整合配置 1.2.1 创建工程,添加依赖和插件 1.2.2 Spring整合Mybati ...

  9. web核心 4-response响应对象 servletContext对象 响应行响应体 请求转发 重新定向 从服务器下载与上传资源 切换验证码 网站统计访问次数

    内容介绍 1 ServletContext对象2 response响应对象 ServletContext对象 概述 ServletContext:servlet的上下文对象(全局管理者) 一个项目有且 ...

  10. SpringBoot使用ResponseBodyAdvice进行统一响应处理

    适用场景 , 返回给调用方一个统一的响应对象 , 即Controller中使用了@ResponseBody注解的方法 , 可以随意返回Object , String , List 等 , 在该对象中进 ...

最新文章

  1. 计算机科学在航空航天仿真模拟,2016年南京航空航天大学计算机科学与技术学院541计算机综合基础之数据结构复试笔试仿真模拟题...
  2. stm32L0工程建立(HAL+IAR,无cubemx)
  3. 服务器升级中不能修改信息,服务器升级页面
  4. 如何做一个懂产品的程序员?
  5. 牛客题霸 [判断一个链表是否为回文结构] C++题解/答案
  6. 邮件发送---SpringBoot
  7. 2.[Yii]创建与设置默认控制器及载入模板
  8. Scrapy学习-25-Scrapyd部署spider
  9. Docker中的Nginx搭建HTTPS环境
  10. python程序多次运行_Python内怎么使同一个.py文件多次运行?
  11. Python之网络爬虫(Xpath语法、Scrapy框架的认识)
  12. HDU 2340 - Obfuscation(dp)
  13. python 中定义的函数 如何在main中调用_在python中,在定义类时自动运行函数的方法?_class_酷徒编程知识库...
  14. Android应用开发以及设计思想深度剖析(2)
  15. 分支定界 matlab,分支定界法matlab程序
  16. myeclipse中ssm的搭建
  17. ENVI5.3下载与安装
  18. 仿站王者:Teleport Ultra
  19. 使用车辆座椅上的压电传感器无创检测呼吸和心率
  20. 宋鸿兵 - 货币战争5(2014年3月20日)

热门文章

  1. GB2312-80 汉字机内码
  2. JointJs快速入门
  3. 手机输入法带拼音声调_这些神奇的拼音输入法,你都知道几个?
  4. Linux系统下安装es中ik分词器详解
  5. MATLAB QPSK调制代码
  6. Unity零基础到入门 ☀️| Unity常用知识点介绍,游戏组成+游戏对象+地形+坐标系等等(必备知识)
  7. 《算法笔记》和《算法笔记上机训练指南》学习笔记汇总
  8. 中国联通5G-NR 900MHz基站设备技术白皮书(2022)
  9. 2017锤子科技发布会
  10. python自动发微信api_调用微信API发送微信消息python脚本