Jackson+Feign反序列化问题排查

概述

本文记录在使用Spring Cloud微服务开发时遇到的一个反序列化问题,RPC/HTTP框架使用的是Feign,JSON序列化反序列化工具是Jackson。

问题

测试环境的ELK告警日志如下:

- [43f42bf7] 500 Server Error for HTTP POST "/api/open/dialog/nextQuestion"
feign.codec.DecodeException: Error while extracting response for type [AbaResponse<UserAccountVO>] 
and content type [application/json;charset=UTF-8]; 
nested exception is org.springframework.http.converter.HttpMessageNotReadableException:
JSON parse error: Expected array or string.; 
nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Expected array or string.
at [Source: (ByteArrayInputStream); line: 1, column: 295] (through reference chain: com.aba.common.utils.context.AbaResponse["data"]->com.aba.enduser.common.vo.UserAccountVO["privacySettings"]->java.util.LinkedHashMap["MINIMUM_LEGAL_AGE"]->com.aba.enduser.common.dto.account.PrivacySettings["timestamp"])
at feign.SynchronousMethodHandler.decode(SynchronousMethodHandler.java:180)
at feign.SynchronousMethodHandler.executeAndDecode(SynchronousMethodHandler.java:140)
at feign.SynchronousMethodHandler.invoke(SynchronousMethodHandler.java:78)
at feign.ReflectiveFeign$FeignInvocationHandler.invoke(ReflectiveFeign.java:103)

报错产生自gateway-open服务,gateway-open服务把接口请求/api/open/dialog/nextQuestion转发到dialog服务,dialog服务在Feign调用另外一个enduser服务时发生。很熟悉的报错,Feign反序列化问题。

排查

no Creators, like default construct, exist: cannot deserialize from Object value no delegate- or property-based Creator

为了排查问题,首先想到本地复现问题。本地启动dialog和enduser服务,postman请求dialog服务的接口/dialog/nextQuestion。却出现另一个问题,且这个报错发生在解析requestBody时。在Controller层方法里第一行加断点,程序都没在断点处停止,直接报错:

Caught unhandled generic exception in com.aba.dialog.controller.DialogController
org.springframework.http.converter.HttpMessageConversionException: Type definition error: [simple type, class com.aba.dialog.service.domain.assessment.dialog.answer.DialogAnswerItem]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `com.aba.dialog.service.domain.assessment.dialog.answer.DialogAnswerItem` (no Creators, like default construct, exist): cannot deserialize from Object value (no delegate- or property-based Creator)at [Source: (PushbackInputStream); line: 1, column: 2]
at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:242)
at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.read(AbstractJackson2HttpMessageConverter.java:227)
at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodArgumentResolver.readWithMessageConverters(AbstractMessageConverterMethodArgumentResolver.java:204)

dialog服务最近没有任何改动啊。enduser服务有改动,也和dialog服务无关;毕竟dialog服务断点没进去。

报错代码:

@PostMapping(value = "/nextQuestion")
public DialogDTO handleDialog(@RequestBody DialogAnswerItem item) {// 断点行String platform = httpServletRequest.getHeader("dialogPlatform");
}

@RequestBody注解的POJO类:

data class DialogAnswerItem(val stateId: StateId,var answer: GivenAnswer,val progress: Double = 0.0,val entryPoint: String? = null)

不甚熟悉的kotlin语言。

看起来一时半会搞不定。

Expected array or string

既然上面的问题没搞定,先解决测试环境的问题。本地启动第三个应用gateway服务,postman模拟调用gateway服务,由gateway负责转发。问题重现:
在这里插入图片描述
诸多分析,Google搜到一个靠谱的stackoverflow答案:feign-client-decodeexception-error-while-extracting-response。

修改enduser服务代码:

@Data
@AllArgsConstructor
@NoArgsConstructor
public class PrivacySettings implements Serializable {private Boolean value;@JsonSerialize(using = LocalDateTimeSerializer.class)@JsonDeserialize(using = LocalDateTimeDeserializer.class)private LocalDateTime timestamp;
}

本地调试,问题解决。

wait but why。

上面也提到【enduser服务有改动,也和dialog服务无关】,现在为了解决Feign + Jackson远程调用反序列化失败问题,去修改enduser代码,增加2个Jackson提供的注解@JsonSerialize@JsonDeserialize

问题虽然解决,总感觉哪里不对劲。但是测试环境里,前端等着使用相关接口,没成多想,发布测试环境。

Feign

结果发布到测试环境后,测试环境里ELK也记录到我一开始在本地调试重现问题时遇到的另外一个问题:
no Creators, like default construct, exist: cannot deserialize from Object value no delegate- or property-based Creator

看来这个问题是绕不过去的坎。诸般Google/百度搜索与尝试,始终没解决问题。

最后还是仔仔细细看Google给出的第一篇stackoverflow文章no-creators-like-default-construct-exist-cannot-deserialize-from-object-valu,看到:

register jackson module kotlin to ObjectMapper.

才突然意识到,最近对一个common-web组件库做了mvn clean deploy操作。deploy包括install,所以本地环境和测试环境都有相同问题。

再检查common-web下面的配置类:

@Component
public class JsonConfig {/*** 解决JSON parse error: Unrecognized field "xxx"异常问题*/@Beanpublic MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();ObjectMapper objectMapper = new ObjectMapper();objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);converter.setObjectMapper(objectMapper);return converter;}
}

如上述代码里注释所述,增加此配置是为了解决JSON: Unrecognized field, not marked as ignorable问题,参考stackoverflow的问答jackson-with-json-unrecognized-field-not-marked-as-ignorable。

之前在另外2个服务都出现过此问题,出现此问题的场景都是A服务调用B服务,B服务在业务开发时增加字段(杜绝修改字段和删除字段的开发bad practice)。A服务在微服务体系里还是在使用旧版本的B-api.jar,也就是说A服务的镜像里的jar里还是使用旧的版本,但是在Feign调用B服务时,B服务返回一个新版本的B-api.jar,多了一个字段。于是报错??

A服务重新编译新版本,则会把新版本的B-api.jar纳入到镜像里,也就是说发布新版本即可解决问题。

想要一劳永逸解决此类问题,在A服务里新增上述配置类就可以了吗?待验证。

考虑到Spring Cloud微服务体系,加字段是很常见的事情,那是不是可以把配置类放在common-web组件库,让所有服务都有此配置类。待验证。

正是因为上述猜想待验证,代码一直在本地。common-web组件库里其他类加以调整时,把JsonConfig配置类编译到dialog服务。

最后,两个问题的解决方法都是移除JsonConfig配置类,并且enduser服务的两个Jackson注解都可以revert。

问题是得以"解决",但是为啥呢?

后面仔细看dialog服务代码,好几个Jackson配置:

@Configuration
@EnableAsync
open class ApplicationConfig {private val log = LoggerFactory.getLogger(this.javaClass)@Beanopen fun restTemplateCommon(): RestTemplate {val restTemplate = RestTemplate()addOwnMappingJackson2HttpMessageConverter(restTemplate)val interceptors = listOf(ClientHttpRequestInterceptor { request, body, execution ->val headers = request.headersheaders.add("Accept", MediaType.APPLICATION_JSON_VALUE)headers.add("Content-Type", MediaType.APPLICATION_JSON_VALUE)execution.execute(request, body)})restTemplate.interceptors = interceptorsreturn restTemplate}private fun addOwnMappingJackson2HttpMessageConverter(restTemplate: RestTemplate) {val converter = MappingJackson2HttpMessageConverter()val objectMapper = ObjectMapper().findAndRegisterModules()// needed that the LocalDate is not serialized to [2000,1,1] but to "2000-01-01".configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false).configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);converter.objectMapper = objectMapperval jacksonMappers = restTemplate.messageConverters.filter { httpMessageConverter -> httpMessageConverter is MappingJackson2HttpMessageConverter }if (jacksonMappers.isNotEmpty()) {restTemplate.messageConverters.remove(jacksonMappers.first())}restTemplate.messageConverters.add(1, converter)}}

上面这个是kotlin语言。以及

@Configuration
public class HttpConverterConfig implements WebMvcConfigurer {@Beanpublic MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {AdaJackson2ObjectMapperBuilder adaJackson2ObjectMapperBuilder = new AdaJackson2ObjectMapperBuilder();return new MappingJackson2HttpMessageConverter(adaJackson2ObjectMapperBuilder.build()) {@Overrideprotected void writeInternal(@NotNull Object object, Type type, @NotNull HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {if (object instanceof String) {Charset charset = this.getDefaultCharset();StreamUtils.copy((String) object, charset, outputMessage.getBody());} else {super.writeInternal(object, type, outputMessage);}}};}
}

以及:

@Component
public class AdaJackson2ObjectMapperBuilder extends Jackson2ObjectMapperBuilder {public AdaJackson2ObjectMapperBuilder() {serializationInclusion(JsonInclude.Include.NON_NULL);serializationInclusion(JsonInclude.Include.NON_ABSENT);featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS,SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS);modules(new AdaModule(), new GuavaModule(), new JavaTimeModule(), new Jdk8Module(), new ParameterNamesModule());}@Overridepublic void configure(@NotNull ObjectMapper objectMapper) {super.configure(objectMapper);// disable constructor, getter and setter detectionobjectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE);objectMapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);objectMapper.registerModule(new KotlinModule());}private static class AdaModule extends SimpleModule {public AdaModule() {addSerializer(JSONError.class, new JSONErrorSerializer());}}
}

以及:

public class JSONErrorSerializer extends JsonSerializer<JSONError> {private static final String KEY_STATUS_CODE = "statusCode";private static final String KEY_ERROR = "error";private static final String KEY_MESSAGE = "message";@Overridepublic void serialize(JSONError jsonError, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {jsonGenerator.writeStartObject();jsonGenerator.writeStringField(KEY_STATUS_CODE, String.valueOf(jsonError.getStatusCode()));jsonGenerator.writeStringField(KEY_ERROR, jsonError.getError());if (jsonError.getMessage() != null && !jsonError.getMessage().isEmpty()) {jsonGenerator.writeStringField(KEY_MESSAGE, jsonError.getMessage());}jsonGenerator.writeEndObject();}
}

参考

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

洗地机哪款最好用?口碑最好的家用洗地机推荐

洗地机方便快捷的清洁方式&#xff0c;如今融入到我们的日常生活需求中来了&#xff0c;然而&#xff0c;在市面上琳琅满目的洗地机品牌中&#xff0c;究竟哪款洗地机比较好用呢&#xff1f;今天&#xff0c;笔者将向大家推荐四款口碑最好的家用洗地机&#xff0c;让你在挑选时…

Java实现防重复提交,使用自定义注解的方式

目录 1.背景 2.思路 3.实现 创建自定义注解 编写拦截器 4.使用 5.验证 6.总结 1.背景 在进行添加操作时&#xff0c;防止恶意点击&#xff0c;后端进行请求接口的防重复提交 2.思路 通过拦截器搭配自定义注解的方式进行实现&#xff0c;拦截器拦截请求&#xff0c;使…

JS加密/解密之webpack打包代码逆向

Webpack 是一个强大的打包工具&#xff0c;能够将多个文件打包成一个或多个最终的文件。然而&#xff0c;将已经经过打包的代码还原回原始源代码并不是一件直接的事情&#xff0c;因为 webpack 打包的过程通常会对代码进行压缩、混淆和优化&#xff0c;丢失了部分变量名和代码结…

Gralloc ION DMABUF in Camera Display

目录 Background knowledge Introduction ia pa va and memory addressing Memory Addressing Page Frame Management Memory area management DMA IOVA and IOMMU Introduce DMABUF What is DMABUF DMABUF 关键概念 DMABUF APIS –The Exporter DMABUF APIS –The…

Flutter - 波浪动画和lottie动画的使用

demo 地址: https://github.com/iotjin/jh_flutter_demo 代码不定时更新&#xff0c;请前往github查看最新代码 波浪动画三方库wave lottie动画 Lottie 是 Airbnb 开发的一款能够为原生应用添加动画效果的开源工具。具有丰富的动画效果和交互功能。 # 波浪动画 https://pub-web…

高并发下的服务容错

在微服务架构中&#xff0c;我们将业务拆分成一个个的服务&#xff0c;服务与服务之间可以相互调用&#xff0c;但是由于网络 原因或者自身的原因&#xff0c;服务并不能保证服务的100%可用&#xff0c;如果单个服务出现问题&#xff0c;调用这个服务就会 出现网络延迟&#xf…

线性回归模型进行特征重要性分析

目的 线性回归是很常用的模型&#xff1b;在局部可解释性上也经常用到。 数据归一化 归一化通常是为了确保不同特征之间的数值范围差异不会对线性模型的训练产生过大的影响。在某些情况下&#xff0c;特征归一化可以提高模型的性能&#xff0c;但并不是所有情况下都需要进行归一…

PG学习笔记(PostgreSQL)

PG学习笔记&#xff08;PostgreSQL&#xff09; 1、PG特点 项目极限值最大单个数据库大小不限最大最大数据单表大小32 TB单条记录最大1.6TB单字段最大允许1GB单表允许最大记录数不限单表最大字段数250~1600&#xff08;取决于字段类型&#xff09;单表最大索引数不限 2、PG安…

go-gin-api 本地部署调试问题总结

1.告警邮箱设置 保存后会自动将配置信息保存在fat_configs.toml 文件中&#xff1b; 可能出现问题&#xff1a;报错 550和 anth 问题&#xff0c;说明你的邮箱配置有问题&#xff08;密码或者授权码&#xff09;&#xff1b; 2.生成数据表curd 执行结果报错 exec: “gormge…

Win10 系统中用户环境变量和系统环境变量是什么作用和区别?

环境&#xff1a; Win10专业版 问题描述&#xff1a; Win10 系统中用户环境变量和系统环境变量是什么作用和区别&#xff1f; 解答&#xff1a; 在Windows 10系统中&#xff0c;用户环境变量和系统环境变量是两个不同的环境变量&#xff0c;它们具有不同的作用和区别 1.用…

UE4中无法保存项目问题

系列文章目录 文章目录 系列文章目录前言一、解决方法 前言 取消&#xff1a;停止保存所有资产并返回编辑器。 重试&#xff1a;尝试再次保存资产。 继续&#xff1a;仅跳过保存该资产。 当我点击继续时&#xff0c;关闭项目&#xff0c;然后重新打开项目&#xff0c;发现之前…

geecg-uniapp 同源策略 数据请求 获取后台数据 进行页面渲染 ui库安装 冲突解决(3)

一&#xff0c;同源策略 &#xff08;1&#xff09;首先找到env 要是没有env 需要创建一个替换成后端接口 &#xff08;2&#xff09;因为他封装了 先找到 http 请求位置一级一级找 然后进行接口修改 &#xff08;3&#xff09;appUpdata 修改接口 运行即可 &#x…

idea中父工程Project创建

1.file-->new-->Project 2.选择maven包和JavaSDK 3.填写项目名&#xff0c;选择文件目录&#xff0c;项目包等 4.配置maven tip&#xff1a;约定>配置>编码 5.设置项目编码 6.注解生效激活&#xff0c;便于项目中使用注解 7.Java编译版本选择8 8.File Type 过滤&a…

【C++STL基础入门】list基本使用

文章目录 前言一、list简介1.1 list是什么1.2 list的头文件 二、list2.1 定义对象2.2 list构造函数2.3 list的属性函数 总结 前言 STL&#xff08;Standard Template Library&#xff09;是C标准库的一个重要组成部分&#xff0c;提供了一套丰富的数据结构和算法&#xff0c;可…

对CU50的修改(未使用)

目的是把CU50中的选择配置拿出来&#xff0c;再把最后BOM的结果拿出来。2023.10.13 一、CU50里面2个标准函数有修改&#xff1a; ----------LCUKOF01----函数----------------CALL FUNCTION CU01_DISPLAY_BOMEXPORTINGmasterdata rcuko-ukompRESULT …

LoRa技术未来发展前景:物联网和边缘计算的引领者

随着物联网和边缘计算的快速发展&#xff0c;低功耗广域网&#xff08;LoRa&#xff09;技术在连接远距离设备、实现长距离通信和满足低功耗需求方面崭露头角。本文将分析LoRa技术在未来的发展前景&#xff0c;尤其是在物联网和边缘计算领域的潜在影响。 LoRa技术的核心优势 1…

python:使用卷积神经网络(CNN)进行回归预测

作者:CSDN @ _养乐多_ 本文详细记录了从Excel或者csv中读取用于训练卷积神经网络(CNN)模型的数据,包括多个自变量和1个因变量数据,以供卷积神经网络模型的训练。随后,我们将测试数据集应用于该CNN模型,进行回归预测和分析。 该代码进一步修改可用于遥感影像回归模型. …

Android位置服务和应用权限

Github:https://github.com/MADMAX110/Odometer 一、使用位置服务 之前的Odometer应用是显示一个随机数&#xff0c;现在要使用Android的位置服务返回走过的距离。 修改getDiatance方法使其返回走过的距离&#xff0c;为此要用Android的位置服务。这些服务允许你得到用户的当…

milvus和相似度检索

流程 milvus的使用流程是 创建collection -> 创建partition -> 创建索引(如果需要检索) -> 插入数据 -> 检索 这里以Python为例, 使用的milvus版本为2.3.x 首先按照库&#xff0c; python3 -m pip install pymilvus Connect from pymilvus import connections c…

12.SpringBoot之RestTemplate的使用

SpringBoot之RestTemplate的使用 初识RestTemplate RestTemplate是Spring框架提供用于调用Rest接口的一个应用&#xff0c;它简化了与http服务通信方式。RestTemplate统一Restfull调用的标准&#xff0c;封装HTTP链接&#xff0c;只要需提供URL及返回值类型即可完成调用。相比…