Spring统一数据返回格式处理String类型出错解析

news2024/12/27 18:39:12

Spring 统一数据返回格式是使用 Spring 进行开发时很常用的一个功能,但是当其处理返回类型原先为 String 类型的时候就会出错报错,需要我们额外对 String 类型进行处理。

例如:现在我开发一个项目,项目中我想要统一返回下述的 Result 类型:

@Data
public class Result {
    private ResultCodeEnum code;
    private String errMsg;
    private Object data;

    public static Result success(Object data) {
        Result result = new Result();
        result.setCode(ResultCodeEnum.SUCCESS);
        result.setErrMsg("");
        result.setData(data);

        return result;
    }
}

此时我就使用到 Spring 的统一数据返回格式功能:

@ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice {

    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        return true;
    }

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        if(body instanceof Result) {
            return body;
        }
        return Result.success(body);
    }
}

但是当我处理原本的返回类型为 String 的时候就出现以下报错:

@RestController
@RequestMapping("/test")
public class TestController {

    @RequestMapping("/m1")
    public String m1() {
        return "hello";
    }
}

其中涉及到的原因就得通过查看 Spring 的源码来进行分析:

SpringMVC 在进行初始化的时候,默认会注册⼀些⾃带的 HttpMessageConverter,MessageConverter 意思是转换器。并且会把它们存进一个名为 messageConverters 的 List 中,因此这些 HttpMessageConverter 存储起来是有先后顺序的,排列之后分别为:
1)ByteArrayHttpMessageConverter
2)StringHttpMessageConverter
3)SourceHttpMessageConverter
4)AllEncompassingFormHttpMessageConverter

这些从 RequestMappingHandlerAdapter 类中的构造方法即可知道:
( 该类正是对应着 SpringMVC 经常使用的路由映射 @RequestMapping 注解 )

其中 AllEncompassingFormHttpMessageConverter 会根据项⽬依赖情况添加对应的 HttpMessageConverter :

在依赖中引⼊jackson相关的 jar 包信息后,容器会把 MappingJackson2HttpMessageConverter ⾃动注册到 messageConverters 这个 List 的末尾。

Spring 会根据我们方法的返回值类型,从 messageConverters 中按顺序找到合适的 HttpMessageConverter 来进行转换。

因此报错原因就很容易找到了:由于 messageConverters 中,String 类的转换器比 JSON 数据格式对应的转换器顺序较前,因此当返回的数据是 String 时, StringHttpMessageConverter 会先被遍历到,这时会使用 StringHttpMessageConverter 。但是实际我们是需要封装成为一个 Result 类来进行返回,而 Spring 对于返回值为对象类型的都会处理返回成 JSON 字符串,因而应该对应的是 JSON 格式数据的转换器 MappingJackson2HttpMessageConverter 。由于转换器使用上出现了错误,势必就导致了使用时出现报错:

我们需要在 AbstractMessageConverterMethodProcessor 这个类中找到 writeWithMessageConverters 这个方法。我们使用统一返回数据格式处理这个功能时,所实现的 ResponseBodyAdvice 接口,具体实现逻辑就是在这个方法内:

找到这个方法中如下图所示的代码处:

上图中标识处就是使用 getAdvice 获取我们最开始代码中定义的 ResponseAdvice 类,并调用我们在该类中实现的 beforeBodyWrite 方法。调用之后一直来到下图中的蓝色标记处就出现了报错:

图中红色标记处,执行完 beforeBodyWrite 方法之后,body 就变成了 Result 类型( 因为我们在beforeBodyWrite 中定义好返回值就是 Result 类型 )。

因而到了蓝色标记处,调用父类( HttpMessageConverter )的 write 方法时,传入的 body 类型就是 Result 类型。

进入 write 方法,并选择 AbstractHttpMessageConverter 所实现的那个:

在这个方法中,使用的是泛型来接收我们传入的 body 参数,因此形参 t 也是 Result 类型,然后再调用 addDefaultHeaders 方法时传入 t 。

进入 addDefaultHeaders 方法,由于 AbstractHttpMessageConverter 的子类 StringHttpMessageConverter 重写了该方法,因此当调用该方法的时候,调用的是子类重写之后的方法( 因为是 StringHttpMessageConverter 引用来进行调用 ):

下述遍历 messageConverters 这个 List ,根据前面所说先遍历到了 StringHttpMessageConverter ,因此此时第二个红色标记处的 converter 指向的是 StringHttpMessageConverter 引用,因此是使用 StringHttpMessageConverter 引用来调用 write 方法,进而 write 内也是使用 StringHttpMessageConverter 引用来调用 addDefaultHeaders 方法。

从上图我们就知道了报错原因:StringHttpMessageConverter 类实现的 addDefaultHeaders 方法的第二个参数是使用 String 类型来进行接收,但是调用处传入的第二个参数 t,根据我们的一步步推断,是一个 Result 类型,类型不匹配,因此出现报错。

因此我们只需对返回值为 String 类型的进行特殊处理即可,使其正常使用 JSON 数据格式对应的转换器而不去使用 String 类型的转换器,类型就不会匹配错误,就不会报错:

@ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice {
    
    private ObjectMapper objectMapper;

    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        return true;
    }

    @SneakyThrows
    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        if(body instanceof Result) {
            return body;
        }
        
        if(body instanceof String) {
            objectMapper.writeValueAsBytes(body);
        }
        return Result.success(body);
    }
}

上述代码中,判断了如果 body 是指向了 String 类型引用,就使用 Spring 内置的 ObjectMapping 对象将其转化为 JSON 字符串再返回。

从 AllEncompassingFormHttpMessageConverter 的构造方法中可以看到,如果返回值是一个 JSON 字符串,则会使用 JSON 字符串对应的转换器,问题解决。

解析重点概括如下:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1326979.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

作业--day35

练习数据库命令 1> 创建一个工人信息库,包含工号(主键)、姓名、年龄、薪资; 2> 添加三条工人信息(可以完整信息,也可以非完整信息); 3> 修改某一个工人的薪资(…

深度学习14—注意力机制与自注意力机制

注:以下均为个人学习笔记,发布只为方便学习阅读,若觉侵权,请联系删除!! 1.李沐老师课堂学习理解笔记 1.1 随意线索和不随意线索 1.2 注意力机制 通过注意力池化层来有偏向性的选择某些输入。 1.3 注意力…

【QT】C++/Qt使用Qt自带工具windeployqt打包

基本操作 运行项目debug或者release 将运行后的可执行文件单独放到一个文件夹中 根据项目使用的kits来选择Qt的打包工具 打开工具后移动到exe文件夹下执行windeployqt xxx.exe 预览图 问题 打包后再其他电脑上运行出现下图错误 将自己电脑的这个文件拷到可执行文件夹中既…

构建高效持久层:深度解析 MyBatis-Plus(02)

目录 引言1. 逻辑删除1.1 概述1.2 逻辑删除的优势1.3.为什么使用逻辑删除1.4 综合案例 2. 乐观锁和悲观锁2.1.什么是乐观锁和悲观锁2.2.乐观锁和悲观锁的区别2.3.综合案例 3. 分页插件总结 引言 在现代软件开发中,数据库操作是不可或缺的一环。为了提高系统的性能、…

网络通信day5作业

1> 使用select完成TCP客户端程序 客户端: #include<myhead.h>#define FPORT 9999 #define FIP "192.168.125.130"#define KPORT 6666 #define KIP "192.168.125.130"int main(int argc, const char *argv[]) {//创建套接字文件描述符int cfd…

关于EasyExcel 合并单元格方法该如何实现

在做一个业务的导出&#xff0c;目前遇到一个需求&#xff0c;如下图&#xff1a; import com.alibaba.excel.metadata.CellData; import com.alibaba.excel.metadata.Head; import com.alibaba.excel.write.handler.CellWriteHandler; import com.alibaba.excel.write.metad…

美好蕴育润康真的对孕妇好吗?有效吗?

在孕期&#xff0c;孕妇的营养摄入对于胎儿的健康发育至关重要。因此&#xff0c;选择一款合适的孕期营养补充品成为了许多孕妇关注的焦点。美好蕴育润康作为一款备受推崇的孕期营养补充品&#xff0c;究竟是否真的对孕妇有益&#xff0c;是否有效呢&#xff1f; 首先&#xff…

数据治理与大模型一体化实践

引言: 大模型落地到当前这个阶段&#xff0c;核心关注点还是领域大模型&#xff0c;而领域大模型落地的前提在于两点&#xff1a;需求端&#xff0c;对当前应用的降本增效以及新应用的探索&#xff1b;供给端&#xff0c;训练技术已经有较高的成熟度。 专家介绍&#xff1a; …

【C语言】指针详解(一)

目录 1.内存和地址 1.1内存 1.2如何理解编址 2.指针变量和地址 2.1取地址操作符&#xff08;&&#xff09; 2.2指针变量和解引用操作符&#xff08;*&#xff09; 2.2.1指针变量 2.2.2拆解指针类型 2.2.3解引用操作符 2.3指针变量大小 1.内存和地址 1.1内存 在讲内…

HarmonyOS引导页登陆页以及tabbar的代码说明1

效果 以下代码是东拼西凑出来的。只是为了个人熟悉一下相关模块的使用&#xff1a; 用的知识点&#xff1a; Resouces 此部分分内容可以在项目中找到&#xff1a; resources/base/element/color.json 为项目着色配置&#xff0c;当然也可以正接在代码里写 float.json 为相关…

如何实现免费无限流量云同步笔记软件Obsidian?

目录 前言 如何实现免费无限流量云同步笔记软件Obsidian&#xff1f; 一、简介 软件特色演示&#xff1a; 二、使用免费群晖虚拟机搭建群晖Synology Drive服务&#xff0c;实现局域网同步 1 安装并设置Synology Drive套件 2 局域网内同步文件测试 三、内网穿透群晖Synol…

上海三思立体育苗系统Vertical X打造“不见光”人工农场

最早的人工育苗可追溯到农耕文明的起源。 人类通过筛选种子、选择播种时机和使用肥料等方式&#xff0c;达到提升产量的效果。随着农业技术发展&#xff0c;育苗床和育苗盆等工具的出现&#xff0c;育苗系统初见雏形。 现代温室育苗技术的应用&#xff0c;给育苗提供了相对更…

为Raspberry Pi OS(Bookworm)设置固定IP

新版的RPI OS采用NetworkManager管理网络&#xff0c;之前/etc/dhcpcd.conf默认找不见了。不过&#xff0c;我们可以使用命令行进行。以我的WIFI为例&#xff0c;演示下如何设置固定IP。 第一步&#xff0c;找到WIFI的内部名称&#xff1a; sudo nmcli -p connection show 注…

爬虫工具Curl!

爬虫工具Curl&#xff01; 链接: Curl 使用它可以将网站内的信息转成python可用格式 打开开发工具中的网络选项卡右键单击&#xff08;或按住 Ctrl 键单击&#xff09;请求单击“复制”→ “复制为 cURL”粘贴到上面的curl命令框中 !!!警告&#xff1a;复制的命令可能包含 co…

DevOps系列文章 : 使用dpkg命令打deb包

创建一个打包的目录&#xff0c;类似rpmbuild&#xff0c;这里创建了目录deb_build mkdir deb_build目标 我有一个hello的二进制文件hello和源码hello.c, 准备安装到/opt/helloworld目录中 步骤 在deb_build目录创建一个文件夹用于存放我的安装文件 mkdir helloworld在he…

使用Pycharm一键将.ui文件生成.py文件配置教程、一键打开QTDesigner教程

2df3621a-7ffd-4f18-9735-b86464b83a5b 前言 我痛恨所有将白嫖归为理所应当的猪&#x1f416;。 教程 打开pycharm之后&#xff0c;依次点击File->Settings->Tools->External Tools&#xff0c;进入如下界面&#xff1a; 1、配置快捷打开Qt Designer 点击号&…

RouterSrv-DHCP

2023年全国网络系统管理赛项真题 模块B-Windows解析 题目 安装和配置DHCP relay服务,为办公区域网络提供地址上网。DHCP服务器位于AppSrv服务器上。拆分DHCP服务器上的作用域,拆分的百分比为7:3。InsideCli优先从RouterSrv获取地址。配置步骤 安装和配置DHCP relay服务,为办…

Python---TCP 网络应用程序开发流程

1. TCP 网络应用程序开发流程的介绍 TCP 网络应用程序开发分为: TCP 客户端程序开发TCP 服务端程序开发 说明: 客户端程序是指运行在用户设备上的程序 服务端程序是指运行在服务器设备上的程序&#xff0c;专门为客户端提供数据服务。 2. TCP 客户端程序开发流程的介绍 步…

【深度学习】注意力机制(七)Agent Attention

本文介绍Agent Attention注意力机制&#xff0c;Transformer中的Attention模块可以提取全局语义信息&#xff0c;但是计算量太大&#xff0c;Agent Attention是一种计算非常有效的Attention模块。 论文&#xff1a;Agent Attention: On the Integration of Softmax and Linear…

虚拟机的下载、安装

下载 vmware workstation&#xff08;收费的虚拟机&#xff09; 下载vbox 网址&#xff1a;Oracle VM VirtualBox&#xff08;免费的虚拟机&#xff09; 以下选择一个下载即可&#xff0c;建议下载vbox&#xff0c;因为是免费的。安装的时候默认下一步即可&#xff08;路径最好…