SpringBoot全局Controller返回值格式统一处理
一、Controller返回值格式统一
1、WebResult类
在 Controller对外提供服务的时候,我们都需要统一返回值格式。一般定义一个 WebResult类。
统一返回值(WebResult类)格式如下:
{"success": true,"code": 200000,"message": null,"data": {"pageList": ["张三","ccc"],"paginator": {"currentPage": 1,"pageSize": 2,"total": 3,"pages": 4}}
}
WebResult类信息:
@Data
@ApiModel(value = "Web结果集")
public class WebResult<T> implements Serializable {private static final long serialVersionUID = -4350499690382193884L;/*** 是否成功, 默认false*/@ApiModelProperty(value = "是否成功, 默认false")private Boolean success = false;/*** 返回状态码*/@ApiModelProperty(value = "返回状态码")private Integer code;/*** 返回信息*/@ApiModelProperty(value = "返回信息")private String message;/*** 返回数据*/@ApiModelProperty(value = "返回数据")private T data;public static <T> WebResult<T> ok() {WebResult<T> webResult = new WebResult<>();webResult.setSuccess(true);webResult.setCode(WebHttpCode.SERVICE_SUCCESS);webResult.setMessage("操作成功");return webResult;}public static <T> WebResult<T> ok(T data) {WebResult<T> webResult = new WebResult<>();webResult.setSuccess(true);webResult.setCode(WebHttpCode.SERVICE_SUCCESS);webResult.setMessage("操作成功");webResult.setData(data);return webResult;}public static <T> WebResult<T> error() {WebResult<T> webResult = new WebResult<>();webResult.setSuccess(false);webResult.setCode(WebHttpCode.SERVICE_ERROR);webResult.setMessage("操作失败");return webResult;}public WebResult message(String message) {this.setMessage(message);return this;}public WebResult data(T data) {this.setData(data);return this;}}
如果我们不做全局 Controller统一处理返回时,就出需要业务在每个 Controller类中返回 WebResult类。同时在全局异常中也返回 WebResult类。
有没有一种跟优雅的方式处理呢?当然有
- 业务不需要对所有 Controller都使用一个返回值(WebResult类),Controller可以需要返回原始值或者自定义的基类,然后处理器统一对返回值(WebResult类)进行封装并输出。
- 同时也可以添加自定义注解,此注解用于忽略返回值封装,按照 Controller原始值返回。
下面我们使用 HandlerMethodReturnValueHandler接口来实现。
2、相关类说明
2.1 HandlerMethodReturnValueHandler接口
使用不同策略处理从调用处理程序方法的返回值,策略处理顶层接口,自定义返回值格式需要实现此接口。
org.springframework.web.method.support.HandlerMethodReturnValueHandler
- supportsReturnType:设置支持返回值类型
- handleReturnValue:处理返回值基础参数
2.2 RequestMappingHandlerAdapter类
请求映射处理适配,包含了参数、返回值处理器等信息
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter
- HandlerMethodReturnValueHandlerComposite内部维护了HandlerMethodReturnValueHandler列表
3.3 RequestResponseBodyMethodProcessor类
属于HandlerMethodReturnValueHandler子类。
org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor
- 主要功能是对请求和响应体的做处理的方法,所有属于RequestResponseBodyMethodProcessor的子类都需要替换为自定义返回值处理
全局Controller返回值统一处理实现原理就是:在bean初始化的时候,获取到所有处理器数组,然后将所有是 RequestResponseBodyMethodProcessor处理器子类对返回值处理的过程替换为自定义返回值处理器处理。
这样当调用对应返回值处理器时,将会使用到自定义的返回值处理器,也就是所有返回值都会按照规定的进行处理。
同时,我们也可以自定义注解(作用于Controller类或者方法级别忽略返回值封装),然后在自定义返回值处理器中忽略返回值封装。
二、全局Controller返回值统一处理实战
1、自定义注解
自定义注解,作用于Controller类或者方法级别忽略返回值封装。
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface IgnoreResponseBodyWrap {
}
2、创建自定义返回值处理器类
创建自定义处理器类实现 HandlerMethodReturnValueHandler接口,主要用于实现自定义返回值内容,不需要注入容器。
下面代码中的 BaseXXXResult是业务的基类。你可以自定义或者使用你们约定的基类。
/*** Controller 返回值格式统一处理*/
public class ResponseBodyWrapHandler implements HandlerMethodReturnValueHandler {private final HandlerMethodReturnValueHandler delegate;public ResponseBodyWrapHandler(HandlerMethodReturnValueHandler delegate) {this.delegate = delegate;}/*** 设置支持返回值类型** @param returnType* @return*/@Overridepublic boolean supportsReturnType(MethodParameter returnType) {return delegate.supportsReturnType(returnType);}/*** 处理返回值基础参数** @param returnValue 方法的返回值对象* @param returnType 封装方法参数说明的辅助类(方法的返回值类型)* @param mavContainer 用于设置模型和视图的容器* @param webRequest 当前的请求对象* @throws Exception*/@Overridepublic void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {// 如果类或者方法含有不包装注解则忽略包装IgnoreResponseBodyWrap ignoreResponseBodyWrap = returnType.getDeclaringClass().getAnnotation(IgnoreResponseBodyWrap.class);if (ignoreResponseBodyWrap != null) {delegate.handleReturnValue(returnValue, returnType, mavContainer, webRequest);return;}ignoreResponseBodyWrap = returnType.getMethodAnnotation(IgnoreResponseBodyWrap.class);if (ignoreResponseBodyWrap != null) {delegate.handleReturnValue(returnValue, returnType, mavContainer, webRequest);return;}// 返回统一格式Object obj = null;if (returnValue instanceof WebResult) {obj = returnValue;} else if (returnValue instanceof BasePageResult) {BasePageResult basePageResult = (BasePageResult) returnValue;ErrorCode errorCode = basePageResult.getErrorCode();Map<String, Object> pageData = new HashMap<>();pageData.put(CommonConstants.PAGE_LIST, basePageResult.getPageList());pageData.put(CommonConstants.PAGINATOR, basePageResult.getPaginator());obj = WebResult.ok().data(pageData).message(errorCode == null ? null : errorCode.getMessage());} else if (returnValue instanceof BaseOperateResult) {BaseOperateResult baseOperateResult = (BaseOperateResult) returnValue;ErrorCode errorCode = baseOperateResult.getErrorCode();boolean success = baseOperateResult.isSuccess();if (success) {obj = WebResult.ok().data(baseOperateResult.getId()).message(errorCode == null ? null : errorCode.getMessage());} else {obj = WebResult.error().message(errorCode == null ? null : errorCode.getMessage());}} else if (returnValue instanceof BaseDataResult) {BaseDataResult baseDataResult = (BaseDataResult) returnValue;ErrorCode errorCode = baseDataResult.getErrorCode();boolean success = baseDataResult.isSuccess();if (success) {obj = WebResult.ok().data(baseDataResult.getData()).message(errorCode == null ? null : errorCode.getMessage());} else {obj = WebResult.error().message(errorCode == null ? null : errorCode.getMessage());}} else if (returnValue instanceof BaseResult) {BaseResult baseResult = (BaseResult) returnValue;ErrorCode errorCode = baseResult.getErrorCode();boolean success = baseResult.isSuccess();if (success) {obj = WebResult.ok().message(errorCode == null ? null : errorCode.getMessage());} else {obj = WebResult.error().message(errorCode == null ? null : errorCode.getMessage());}} else {obj = returnValue;}delegate.handleReturnValue(obj, returnType, mavContainer, webRequest);}
}
3、注册自定义返回值处理器类
将所有 RequestResponseBodyMethodProcessor返回值处理器替换为自定义的返回值处理器。
@Component
public class ResponseBodyWrapFactoryBean implements InitializingBean {private final RequestMappingHandlerAdapter adapter;@Autowiredpublic ResponseBodyWrapFactoryBean(RequestMappingHandlerAdapter adapter) {this.adapter = adapter;}@Overridepublic void afterPropertiesSet() throws Exception {List<HandlerMethodReturnValueHandler> returnValueHandlers = adapter.getReturnValueHandlers();if (returnValueHandlers.size() > 0) {// 将内置的返回值处理器进行替换List<HandlerMethodReturnValueHandler> handlers = new ArrayList<>(returnValueHandlers);decorateHandlers(handlers);adapter.setReturnValueHandlers(handlers);}}/*** 将所有RequestResponseBodyMethodProcessor返回值处理器替换为自定义的返回值处理器** @author tianxincode@163.com* @since 2020/10/12*/private void decorateHandlers(List<HandlerMethodReturnValueHandler> handlers) {for (HandlerMethodReturnValueHandler handler : handlers) {if (handler instanceof RequestResponseBodyMethodProcessor) {// 替换为自定义返回值处理器ResponseBodyWrapHandler decorator = new ResponseBodyWrapHandler(handler);int index = handlers.indexOf(handler);handlers.set(index, decorator);break;}}}}
4、全局异常处理类
一般项目都会有一个全局异常处理类
@Slf4j
@ResponseBody
@ControllerAdvice
public class GlobalExceptionHandler {@ExceptionHandler(value = Exception.class)private WebResult handlerException(Exception e) {log.error("Exception 异常 -> error:", e);WebResult webResult = new WebResult();webResult.setSuccess(false);webResult.setCode(WebHttpCode.SYSTEM_ERROR);webResult.setMessage("系统异常,请联系管理员");return webResult;}@ExceptionHandler(value = RuntimeException.class)private WebResult handlerRuntimeException(RuntimeException e) {log.error("RuntimeException 异常 -> error:", e);WebResult webResult = new WebResult();webResult.setSuccess(false);webResult.setCode(WebHttpCode.SYSTEM_ERROR);webResult.setMessage("系统异常");return webResult;}@ExceptionHandler(value = ServiceException.class)private WebResult handlerServiceException(ServiceException e) {log.error("ServiceException 异常 -> error:", e);WebResult webResult = new WebResult();webResult.setSuccess(false);webResult.setCode(WebHttpCode.SERVICE_ERROR);webResult.setMessage(e.getMessage());return webResult;}}
5、Controller示例
@RestController
@RequestMapping("/project2")
@Slf4j
@Api(value = "Project2Controller", tags = {"Project2相关操作接口"})
public class Project2Controller {@GetMapping("/successPage")@ApiOperation(value = "successPage接口")public BaseResult successPage() {log.info("successPage接口");List list = new ArrayList();list.add("张三");list.add("ccc");BasePageResult basePageResult = new BasePageResult<>();basePageResult.setSuccess(Boolean.TRUE);basePageResult.setPageList(list);basePageResult.setPaginator(new Paginator(1, 2, 3, 4));return basePageResult;}@GetMapping("/get")@ApiOperation(value = "get接口")@IgnoreResponseBodyWrappublic List get() {log.info("get接口");ProjectDO projectDO = new ProjectDO();projectDO.setId(10L);projectDO.setName("项目10");projectDO.setDepartmentId(0L);projectDO.setDescr("");projectDO.setDelFlag(false);projectDO.setCreateTime(new Date());projectDO.setUpdateTime(new Date());List list = new ArrayList();list.add("张三");list.add("ccc");return list;}@GetMapping("/get2")@ApiOperation(value = "get2接口")public BaseDataResult get2() {log.info("get2接口");ProjectDO projectDO = new ProjectDO();projectDO.setId(10L);projectDO.setName("项目10");projectDO.setDepartmentId(0L);projectDO.setDescr("");projectDO.setDelFlag(false);projectDO.setCreateTime(new Date());projectDO.setUpdateTime(new Date());BaseDataResult baseDataResult = new BaseDataResult();baseDataResult.setSuccess(Boolean.TRUE);baseDataResult.setData(projectDO);return baseDataResult;}/*** 文件下载接口*/@GetMapping("/download")@ApiOperation(value = "文件下载接口")public void download(HttpServletResponse response) throws SerialException {try {// 获取要下载的文件File file = new File("D:\\TempFiles\\xxxxxx.xlsx");String fileName = file.getName();response.setContentType("application/octet-stream;charset=UTF-8");response.setHeader("Content-Disposition", "attachment;filename=" + new String(fileName.getBytes("UTF-8"), "iso-8859-1"));IOUtils.copy(new FileInputStream(file), response.getOutputStream());} catch (Exception e) {log.info("文件下载异常 -> e=", e);throw new ServiceException("文件下载异常");}}@GetMapping("/systemError")@ApiOperation(value = "systemError接口")public BaseResult systemError() {log.info("systemError接口");int i = 2 / 0;BaseResult baseResult = new BaseResult();baseResult.setSuccess(Boolean.TRUE);return baseResult;}}
– 求知若饥,虚心若愚。
相关文章:

SpringBoot全局Controller返回值格式统一处理
一、Controller返回值格式统一 1、WebResult类 在 Controller对外提供服务的时候,我们都需要统一返回值格式。一般定义一个 WebResult类。 统一返回值(WebResult类)格式如下: {"success": true,"code": 2…...

程序媛的mac修炼手册-- 终端shell的驾驭 zsh vs bash
进入终端(Terminal)为新下载的应用配置环境,是Mac生产力up up的关键一步,更是编程小白装大神的第一步。Fake it till you make it , 硅谷大神标准路径~ shell的基本原理 为应用配置环境,相当于在应用和操作系统间架桥。由此&…...

基于PHP的校园代购商城系统
有需要请加文章底部Q哦 可远程调试 基于PHP的校园代购商城系统 一 介绍 此校园代购商城系统基于原生PHP开发,数据库mysql,前端bootstrap。系统角色分为用户和管理员。(附带参考设计文档) 技术栈:phpmysqlbootstrapphpstudyvscode 二 功能 …...

感知与认知的碰撞,大模型时代的智能文档处理范式
目录 0 写在前面1 GPT4-V:拓宽文档认知边界2 大语言模型的文档感知缺陷3 大一统文档图像处理范式3.1 像素级OCR任务3.2 OCR大一统模型3.3 长文档理解与应用 4 总结抽奖福利 0 写在前面 由中国图象图形学学会青年工作委员会发起的第十九届中国图象图形学学会青年科学…...
ECMAScript和JavaScript的区别
ECMAScript和JavaScript之间的关系和差异可以从以下几个方面来理解: 定义: ECMAScript:ECMAScript是一种由Ecma国际(前身为欧洲计算机制造商协会,英文名称是European Computer Manufacturers Association)通…...

[BUG]Datax写入数据到psql报不能序列化特殊字符
1.问题描述 Datax从mongodb写入数据到psql报错如下 org.postgresql.util.PSQLException: ERROR: invalid bytesequence for encoding "UTF8": 0x002.原因分析 此为psql独有的错误,不能对特殊字符’/u0000’,进行序列化,需要将此特殊字符替…...
用数据结构python写大数计算器
下面是一个基于Python的大数计算器的示例代码: class BigNumberCalculator:def __init__(self, num1, num2):self.num1 num1self.num2 num2staticmethoddef add(num1, num2):result carry 0len1, len2 len(num1), len(num2)max_len max(len1, len2)for i in …...

08.哲说建造者模式(Builder Pattern)
“The odds that we’re in ‘base reality’ is one in billions.” —— Elon Musk 这段话出自马斯克在2016年的一次演讲,“人类活在真实世界的几率,可能不到十亿分之一”。此言一出,可谓一石激起千层浪。有人嘲讽马斯克是“语不惊人死不休…...

ubuntu18.04查询实时内存、CPU占用率命令
gnome-system-monitor效果就是下面这样:...

Python计算圆的面积
Python 计算圆的面积 圆的面积公式为 : 公式中 r 为圆的半径。 # 定义一个方法来计算圆的面积 def findArea(r): PI 3.142 return PI * (r*r) # 调用方法 r float( input("请输入圆的半径:") ) print( "圆的面积为 %.3f&qu…...

(Java企业 / 公司项目)Nacos的怎么搭建多环境配置?(含相关面试题)(二)
上一篇讲了一个单体服务中配置,传统的Nacos配置但是在微服务架构当中肯定都是多环境下配置,比如生产环境,dev测试环境等等。 第一种方式模拟开始: 首先展示在生产环境中nacos如何配置,在模块下新建一个配置文件&…...

DolphinScheduler实际应用
前言 最近公司新启动了一个项目,然后领导想用一下新技术,并且为公司提供多个大数据调度解决方案,我呢就根据领导要求调研了下当前的开源调度工具,最终决定采用DolphinScheduler, 因此研究了一下DolphinScheduler &…...

P10 RV1126推流项目——ffmpeg输出参数初始化
前言 从本章开始我们将要学习嵌入式音视频的学习了 ,使用的瑞芯微的开发板 🎬 个人主页:ChenPi 🐻推荐专栏1: 《C_ChenPi的博客-CSDN博客》✨✨✨ 🔥 推荐专栏2: 《Linux C应用编程(概念类)_C…...
正定矩阵在格密码中的应用(知识铺垫)
目录 一. 写在前面 二. 最小值点 三. 二次型结构 四. 正定与非正定讨论 4.1 对参数a的要求 4.2 对参数c的要求 4.3 对参数b的要求 五. 最小值,最大值与奇异值 5.1 正定型(positive definite) 5.2 负定型(negative defin…...

关于使用Selenium获取网页控制台的数据
背景: 需要获取网页的控制台的数据,如下图 在此文章将使用到 Pycharm 和 Selenium4 Pycharm安装 Selenium安装 from selenium import webdriver from selenium.webdriver.common.by import By import time# 创建浏览器对象 browser webdriver.Chro…...
vue2和vue3中的路由使用及传参方式
文章目录 vue2中使用路由Vue3 中使用路由路由传参方式 Vue 2 和 Vue 3 中的路由系统有很多相似之处,但也存在一些重要的区别。下面将分别介绍 Vue 2 和 Vue 3 中的路由使用方式,并了解下它们之间的不同之处。 vue2中使用路由 在 Vue 2 中,通…...

论文管理器
论文管理器 这个论文管理器仍然存在许多漏洞。目前,通过按照一些例行程序操作,它可以正常工作。我将在有时间的时候改进代码,提供详细说明,并添加新功能。当该管理器的代码进行优化后,我会上传到github上。 一个建立…...
postfix配置tls加密
1.编译安装 编译安装openss【卸载原有openssl,然后下载新的安装,因为postfix需要新版本openssl】编译安装postfix,下面这行命令 make -f Makefile.init makefiles CCARGS"-DHAS_MYSQL -I/www/server/mysql/include -DUSE_SASL_AUTH -I/usr/include…...

虚拟专线网络(IP-VPN)
虚拟专线网络(IP-VPN),因为它的安全性和可靠性。通过亚洲领先的 IP VPN 提供商。享受更高的可管理性和可扩展性,在多个站点之间交付 IP 流量或数据包,拥有亚太地区最大的 IP 骨干网。 1,保证正常运行时间,在网络链路发…...

【Unity动画系统】Unity动画系统Animation详解,参数细节你是否弄清?
👨💻个人主页:元宇宙-秩沅 👨💻 hallo 欢迎 点赞👍 收藏⭐ 留言📝 加关注✅! 👨💻 本文由 秩沅 原创 👨💻 收录于专栏:Uni…...

SpringBoot-17-MyBatis动态SQL标签之常用标签
文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…...

Lombok 的 @Data 注解失效,未生成 getter/setter 方法引发的HTTP 406 错误
HTTP 状态码 406 (Not Acceptable) 和 500 (Internal Server Error) 是两类完全不同的错误,它们的含义、原因和解决方法都有显著区别。以下是详细对比: 1. HTTP 406 (Not Acceptable) 含义: 客户端请求的内容类型与服务器支持的内容类型不匹…...
Nginx server_name 配置说明
Nginx 是一个高性能的反向代理和负载均衡服务器,其核心配置之一是 server 块中的 server_name 指令。server_name 决定了 Nginx 如何根据客户端请求的 Host 头匹配对应的虚拟主机(Virtual Host)。 1. 简介 Nginx 使用 server_name 指令来确定…...
TRS收益互换:跨境资本流动的金融创新工具与系统化解决方案
一、TRS收益互换的本质与业务逻辑 (一)概念解析 TRS(Total Return Swap)收益互换是一种金融衍生工具,指交易双方约定在未来一定期限内,基于特定资产或指数的表现进行现金流交换的协议。其核心特征包括&am…...
Element Plus 表单(el-form)中关于正整数输入的校验规则
目录 1 单个正整数输入1.1 模板1.2 校验规则 2 两个正整数输入(联动)2.1 模板2.2 校验规则2.3 CSS 1 单个正整数输入 1.1 模板 <el-formref"formRef":model"formData":rules"formRules"label-width"150px"…...
React---day11
14.4 react-redux第三方库 提供connect、thunk之类的函数 以获取一个banner数据为例子 store: 我们在使用异步的时候理应是要使用中间件的,但是configureStore 已经自动集成了 redux-thunk,注意action里面要返回函数 import { configureS…...

AI病理诊断七剑下天山,医疗未来触手可及
一、病理诊断困局:刀尖上的医学艺术 1.1 金标准背后的隐痛 病理诊断被誉为"诊断的诊断",医生需通过显微镜观察组织切片,在细胞迷宫中捕捉癌变信号。某省病理质控报告显示,基层医院误诊率达12%-15%,专家会诊…...

JVM虚拟机:内存结构、垃圾回收、性能优化
1、JVM虚拟机的简介 Java 虚拟机(Java Virtual Machine 简称:JVM)是运行所有 Java 程序的抽象计算机,是 Java 语言的运行环境,实现了 Java 程序的跨平台特性。JVM 屏蔽了与具体操作系统平台相关的信息,使得 Java 程序只需生成在 JVM 上运行的目标代码(字节码),就可以…...
腾讯云V3签名
想要接入腾讯云的Api,必然先按其文档计算出所要求的签名。 之前也调用过腾讯云的接口,但总是卡在签名这一步,最后放弃选择SDK,这次终于自己代码实现。 可能腾讯云翻新了接口文档,现在阅读起来,清晰了很多&…...

《信号与系统》第 6 章 信号与系统的时域和频域特性
目录 6.0 引言 6.1 傅里叶变换的模和相位表示 6.2 线性时不变系统频率响应的模和相位表示 6.2.1 线性与非线性相位 6.2.2 群时延 6.2.3 对数模和相位图 6.3 理想频率选择性滤波器的时域特性 6.4 非理想滤波器的时域和频域特性讨论 6.5 一阶与二阶连续时间系统 6.5.1 …...