当前位置: 首页 > news >正文

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对外提供服务的时候&#xff0c;我们都需要统一返回值格式。一般定义一个 WebResult类。 统一返回值&#xff08;WebResult类&#xff09;格式如下&#xff1a; {"success": true,"code": 2…...

程序媛的mac修炼手册-- 终端shell的驾驭 zsh vs bash

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

基于PHP的校园代购商城系统

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

感知与认知的碰撞,大模型时代的智能文档处理范式

目录 0 写在前面1 GPT4-V&#xff1a;拓宽文档认知边界2 大语言模型的文档感知缺陷3 大一统文档图像处理范式3.1 像素级OCR任务3.2 OCR大一统模型3.3 长文档理解与应用 4 总结抽奖福利 0 写在前面 由中国图象图形学学会青年工作委员会发起的第十九届中国图象图形学学会青年科学…...

ECMAScript和JavaScript的区别

ECMAScript和JavaScript之间的关系和差异可以从以下几个方面来理解&#xff1a; 定义&#xff1a; ECMAScript&#xff1a;ECMAScript是一种由Ecma国际&#xff08;前身为欧洲计算机制造商协会&#xff0c;英文名称是European Computer Manufacturers Association&#xff09;通…...

[BUG]Datax写入数据到psql报不能序列化特殊字符

1.问题描述 Datax从mongodb写入数据到psql报错如下 org.postgresql.util.PSQLException: ERROR: invalid bytesequence for encoding "UTF8": 0x002.原因分析 此为psql独有的错误&#xff0c;不能对特殊字符’/u0000’,进行序列化&#xff0c;需要将此特殊字符替…...

用数据结构python写大数计算器

下面是一个基于Python的大数计算器的示例代码&#xff1a; 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年的一次演讲&#xff0c;“人类活在真实世界的几率&#xff0c;可能不到十亿分之一”。此言一出&#xff0c;可谓一石激起千层浪。有人嘲讽马斯克是“语不惊人死不休…...

ubuntu18.04查询实时内存、CPU占用率命令

gnome-system-monitor效果就是下面这样&#xff1a;...

Python计算圆的面积

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

(Java企业 / 公司项目)Nacos的怎么搭建多环境配置?(含相关面试题)(二)

上一篇讲了一个单体服务中配置&#xff0c;传统的Nacos配置但是在微服务架构当中肯定都是多环境下配置&#xff0c;比如生产环境&#xff0c;dev测试环境等等。 第一种方式模拟开始&#xff1a; 首先展示在生产环境中nacos如何配置&#xff0c;在模块下新建一个配置文件&…...

DolphinScheduler实际应用

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

P10 RV1126推流项目——ffmpeg输出参数初始化

前言 从本章开始我们将要学习嵌入式音视频的学习了 &#xff0c;使用的瑞芯微的开发板 &#x1f3ac; 个人主页&#xff1a;ChenPi &#x1f43b;推荐专栏1: 《C_ChenPi的博客-CSDN博客》✨✨✨ &#x1f525; 推荐专栏2: 《Linux C应用编程&#xff08;概念类&#xff09;_C…...

正定矩阵在格密码中的应用(知识铺垫)

目录 一. 写在前面 二. 最小值点 三. 二次型结构 四. 正定与非正定讨论 4.1 对参数a的要求 4.2 对参数c的要求 4.3 对参数b的要求 五. 最小值&#xff0c;最大值与奇异值 5.1 正定型&#xff08;positive definite&#xff09; 5.2 负定型&#xff08;negative defin…...

关于使用Selenium获取网页控制台的数据

背景&#xff1a; 需要获取网页的控制台的数据&#xff0c;如下图 在此文章将使用到 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 中的路由系统有很多相似之处&#xff0c;但也存在一些重要的区别。下面将分别介绍 Vue 2 和 Vue 3 中的路由使用方式&#xff0c;并了解下它们之间的不同之处。 vue2中使用路由 在 Vue 2 中&#xff0c;通…...

论文管理器

论文管理器 这个论文管理器仍然存在许多漏洞。目前&#xff0c;通过按照一些例行程序操作&#xff0c;它可以正常工作。我将在有时间的时候改进代码&#xff0c;提供详细说明&#xff0c;并添加新功能。当该管理器的代码进行优化后&#xff0c;我会上传到github上。 一个建立…...

postfix配置tls加密

1.编译安装 编译安装openss【卸载原有openssl&#xff0c;然后下载新的安装&#xff0c;因为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)&#xff0c;因为它的安全性和可靠性。通过亚洲领先的 IP VPN 提供商。享受更高的可管理性和可扩展性&#xff0c;在多个站点之间交付 IP 流量或数据包&#xff0c;拥有亚太地区最大的 IP 骨干网。 1&#xff0c;保证正常运行时间&#xff0c;在网络链路发…...

【Unity动画系统】Unity动画系统Animation详解,参数细节你是否弄清?

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;Uni…...

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…...

DAY 47

三、通道注意力 3.1 通道注意力的定义 # 新增&#xff1a;通道注意力模块&#xff08;SE模块&#xff09; class ChannelAttention(nn.Module):"""通道注意力模块(Squeeze-and-Excitation)"""def __init__(self, in_channels, reduction_rat…...

系统设计 --- MongoDB亿级数据查询优化策略

系统设计 --- MongoDB亿级数据查询分表策略 背景Solution --- 分表 背景 使用audit log实现Audi Trail功能 Audit Trail范围: 六个月数据量: 每秒5-7条audi log&#xff0c;共计7千万 – 1亿条数据需要实现全文检索按照时间倒序因为license问题&#xff0c;不能使用ELK只能使用…...

【CSS position 属性】static、relative、fixed、absolute 、sticky详细介绍,多层嵌套定位示例

文章目录 ★ position 的五种类型及基本用法 ★ 一、position 属性概述 二、position 的五种类型详解(初学者版) 1. static(默认值) 2. relative(相对定位) 3. absolute(绝对定位) 4. fixed(固定定位) 5. sticky(粘性定位) 三、定位元素的层级关系(z-i…...

uniapp微信小程序视频实时流+pc端预览方案

方案类型技术实现是否免费优点缺点适用场景延迟范围开发复杂度​WebSocket图片帧​定时拍照Base64传输✅ 完全免费无需服务器 纯前端实现高延迟高流量 帧率极低个人demo测试 超低频监控500ms-2s⭐⭐​RTMP推流​TRTC/即构SDK推流❌ 付费方案 &#xff08;部分有免费额度&#x…...

Swagger和OpenApi的前世今生

Swagger与OpenAPI的关系演进是API标准化进程中的重要篇章&#xff0c;二者共同塑造了现代RESTful API的开发范式。 本期就扒一扒其技术演进的关键节点与核心逻辑&#xff1a; &#x1f504; 一、起源与初创期&#xff1a;Swagger的诞生&#xff08;2010-2014&#xff09; 核心…...

佰力博科技与您探讨热释电测量的几种方法

热释电的测量主要涉及热释电系数的测定&#xff0c;这是表征热释电材料性能的重要参数。热释电系数的测量方法主要包括静态法、动态法和积分电荷法。其中&#xff0c;积分电荷法最为常用&#xff0c;其原理是通过测量在电容器上积累的热释电电荷&#xff0c;从而确定热释电系数…...

Rust 开发环境搭建

环境搭建 1、开发工具RustRover 或者vs code 2、Cygwin64 安装 https://cygwin.com/install.html 在工具终端执行&#xff1a; rustup toolchain install stable-x86_64-pc-windows-gnu rustup default stable-x86_64-pc-windows-gnu ​ 2、Hello World fn main() { println…...

快速排序算法改进:随机快排-荷兰国旗划分详解

随机快速排序-荷兰国旗划分算法详解 一、基础知识回顾1.1 快速排序简介1.2 荷兰国旗问题 二、随机快排 - 荷兰国旗划分原理2.1 随机化枢轴选择2.2 荷兰国旗划分过程2.3 结合随机快排与荷兰国旗划分 三、代码实现3.1 Python实现3.2 Java实现3.3 C实现 四、性能分析4.1 时间复杂度…...

[USACO23FEB] Bakery S

题目描述 Bessie 开了一家面包店! 在她的面包店里&#xff0c;Bessie 有一个烤箱&#xff0c;可以在 t C t_C tC​ 的时间内生产一块饼干或在 t M t_M tM​ 单位时间内生产一块松糕。 ( 1 ≤ t C , t M ≤ 10 9 ) (1 \le t_C,t_M \le 10^9) (1≤tC​,tM​≤109)。由于空间…...