baseresponse响应类_SpringBoot统一响应体解决方案
前言
最近在优化自己之前基于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统一响应体解决方案相关推荐
- baseresponse响应类_Java response响应体和文件下载实现原理
通过response 设置响应体: 响应体设置文本: PrintWriter getWriter() 获得字符流,通过字符流的write(String s)方法可以将字符串设置到response 缓冲 ...
- 基于Spring AOP的统一响应体的实现(注解版)
基于Spring AOP的统一响应体的实现(注解版) 一.前言 在上一篇系列中 我们 统一参数校验,统一结果响应,统一异常处理,统一错误处理,统一日志记录,统一生成api文档, 对于统一数据响应返回规 ...
- spring boot / cloud (二) 规范响应格式以及统一异常处理
spring boot / cloud (二) 规范响应格式以及统一异常处理 前言 为什么规范响应格式? 我认为,采用预先约定好的数据格式,将返回数据(无论是正常的还是异常的)规范起来,有助于提高团队 ...
- 2022-05-02 一.统一响应码
统一响应码 前言 统一响应码 响应状态 响应结果 例子 前言 作为后端服务器,返回给前端的数据需要统一格式,一般为: {code:200,msg: "接口请求成功",data:.. ...
- baseresponse响应类_内部类、响应类Response、序列化基类、反序列化、全局局部钩子...
一.内部类 1.概念:将类定义在一个类的内部,被定义的类就是内部类 2.特点:内部类及内部类的所以名称空间,可以直接被外部类访问的 3. 应用:通过内部类的名称空间,给外部类额外拓展一些特殊的属性(配 ...
- Servlet基础:接口、类、请求响应、配置、会话追踪、上下文、协作、异常
10.1 Servlet介绍 Servlet技术是Sun公司提供的一种实现动态网页的解决方案,它是基于Java编程语言的Web服务器端编程技术,主要用于在Web服务器端获得客户端的访问请求信息和动 ...
- SpringCloud采用Jackson序列化统一响应不正当的消息转换器导致的异常问题
SpringCloud采用Jackson序列化统一响应不正当的消息转换器导致的异常问题 环境说明 org.springframework.cloud.spring-cloud-dependencies ...
- 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 ...
- web核心 4-response响应对象 servletContext对象 响应行响应体 请求转发 重新定向 从服务器下载与上传资源 切换验证码 网站统计访问次数
内容介绍 1 ServletContext对象2 response响应对象 ServletContext对象 概述 ServletContext:servlet的上下文对象(全局管理者) 一个项目有且 ...
- SpringBoot使用ResponseBodyAdvice进行统一响应处理
适用场景 , 返回给调用方一个统一的响应对象 , 即Controller中使用了@ResponseBody注解的方法 , 可以随意返回Object , String , List 等 , 在该对象中进 ...
最新文章
- VPC DHCP类型的ECS修改DNS
- 做工程师不懂这七点,难怪你总是混不好
- 提防iostream使用中的一个“陷阱”
- hunnu---11547 你的组合数学学得如何?
- mysql批量修改字段字符集_MySQL字符集修改实战教程
- 操作系统原理之内存管理(第四章第一部分)
- 积温空间分布数据、气温分布数据、日照数据、降雨量分布、太阳辐射数据、地表径流数据、土地利用数据、npp数据、ndvi数据
- 依赖的包_运维丨python安装mysql的依赖包mysqlpython操作
- windows命令全集
- Python合成PDF文件
- 老毛子 K2 通过SSH 抓包
- HCL打开显示当前系统用户怎么解决_iPhone8手机变成白苹果怎么办?
- ADB常用命令(adb常用命令)
- 中国大学慕课公开课-《视听语言》-学习笔记-6
- html发展时间轴纵向插件,jquery响应式垂直时间轴插件vertical-timeline
- 使用 TreeView IE Web 控件
- Fluter基础巩固之Dart语言详解一
- 原来她在我的生命中如此重要
- [Android/Linux]-1.power_supply框架初识
- linux怎么进入绘图模式,Linux 绘图工具
热门文章
- ktv服务器管理系统,小型KTV综合解决方案
- 一起学OCP:oracle-082题库及解析(1-20)
- jenkins教程菜鸟_jenkins新手入门教程
- 批量模糊匹配的三种方法
- 微星组件环境linux,微星笔记本常用系统环境组件下载集合
- 计算机国二全称,计算机国二的全称是什么?
- python网络爬虫实战——实时抓取西刺免费代理ip
- 免费代理ip网站总结
- python句柄无效_python免注册调用大漠出现错误句柄无效
- C#二进制方式(binary、varbinary、blob、longblog等)读写mysql