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

“String到Date转换失败”:深挖@RequestBody的日期坑

01 引言

服务端接收参数的形式有多种,可以通过Form表单的形式接收,也可以使用@RequestBody来接收Json参数。但是接收参数过程中,唯独日期类型的参数总是出现解析异常。如下图:

近日,遇到同事求助说通过@RequestBody接受日期参数异常,如上图。项目框架是我搭建的,当时已经处理了关于日期参数传递的解析问题,还是出现问题有点不可思议。

我们一起来看看,怎么解决!

02 全局日期处理器

搭建框架初期,专门通过@InitBinder@ControllerAdvice处理了日期,作为全局的日期处理器。

@Slf4j
@ControllerAdvice
public class GlobalWebBinderHandler {/***  处理web的data数据,这里主要处理form表单的日期*/@InitBinderpublic void globalWebDataBinder(WebDataBinder binder){binder.registerCustomEditor(Date.class, new PropertyEditorSupport() {@Overridepublic void setAsText(String text) throws IllegalArgumentException {Date date = null;try {if (StringUtils.isNotBlank(text)) {if (StringUtils.isNumeric(text)) {date = Date.from(Instant.ofEpochMilli(Long.parseLong(text)));}else {date = DateUtil.parseDate(text);}}} catch (ParseException e) {log.warn("web 日期参数解析异常,异常格式:{}, 支持的日期格式:{}",  text, JSON.toJSONString(DateUtil.GENERIC_DATE_PATTERNS));}setValue(date);}});}
}
  • @ControllerAdvice:用来拦截所有的@Controller控制层
  • @InitBinder:用来处理Form表单传递的参数
  • WebDataBinder:用来映射String参数为T,这里只处理了Date

DateUtil.parseDate(text)是通过DateUtil.GENERIC_DATE_PATTERN数组,依次解析日期,直到日期被正确解析,都则接收的值为NULL

通过@InitBinder注解的注释可以看到,支持RequestMapping的所有参数,尤其Form表单参数。

而从遇到的问题看,这里的配置并没有走到。而是Jackson反序列化时遇到了异常。

03 @RequestBody

同时遇到的异常就是使用@RequestBody来接收JSON数据而导致的。这是怎么产生的呢?@RequestBody 是一个 Spring MVC 注解,它的主要作用是将 HTTP 请求体(Body)中的数据,按照特定的格式(如 JSON、XML)绑定到控制器方法的参数上。它通常用于处理 POSTPUTPATCH 等非 GET 请求,这些请求的数据通常放在请求体中。

3.1 完整的解析流程

整个解析过程可以看作一条清晰的流水线,涉及了从 HTTP 请求到 Java 对象转化的每一步。下图清晰地展示了这一核心流程:

详细说明:

接收请求与分发

  • HTTP 请求到达:客户端发送一个 HTTP 请求(例如,Content-Type: application/json)。
  • DispatcherServlet 拦截:作为前端控制器,DispatcherServlet 会拦截所有请求。
  • 查找处理器DispatcherServlet 根据请求 URL,通过 HandlerMapping 找到对应的控制器(Controller)和处理方法(HandlerMethod)。

准备调用处理方法

  • HandlerAdapter 接手DispatcherServlet 将请求交给 RequestMappingHandlerAdapter 来处理。
  • 解析方法参数HandlerAdapter 开始准备调用目标方法。它需要解析方法的每一个参数,并为每个参数赋值。这时,它会使用一系列 HandlerMethodArgumentResolver(参数解析器)

识别 @RequestBody 注解

  • 找到合适的解析器HandlerAdapter 遍历所有参数解析器,当它遇到一个带有 @RequestBody 注解的参数时,会找到一个特定的解析器 —— RequestResponseBodyMethodProcessor
  • RequestResponseBodyMethodProcessor 工作:这个解析器专门负责处理 @RequestBody@ResponseBody

读取请求体与内容协商

  • 获取输入流RequestResponseBodyMethodProcessorHttpServletRequest 中获取请求体的输入流。
  • 内容协商:它检查请求的 Content-Type头(例如 application/json),以确定客户端发送的数据格式。
  • 选择消息转换器:根据 Content-Type,它会从配置好的 HttpMessageConverter 列表中选择一个合适的转换器。对于 application/json,默认使用的是 MappingJackson2HttpMessageConverter(如果 Jackson 库在类路径上)。

反序列化与类型转换

  • 反序列化(核心步骤)MappingJackson2HttpMessageConverter 从输入流中读取原始的 JSON 字符串,使用 Jackson 的 ObjectMapper 来执行反序列化。

数据验证

  • 执行校验:如果参数上同时还使用了 @Valid@Validated 注解,RequestResponseBodyMethodProcessor 会在此刻触发 JSR-303 Bean 验证。

方法调用与返回

  • 参数绑定完成:此时,一个完整的、填充好数据的 Java 对象已经准备就绪
  • 调用控制器方法HandlerAdapter 用这个对象作为参数,调用你的控制器方法。
  • 处理响应:方法执行完毕后,返回结果,流程进入 @ResponseBody 的序列化流程。

3.2 关键代码追踪

通过跟踪代码,大致的流程如上图。

根据断点调试,我们会发现默认的MessageConverters有8个,第6个和第7个是专门解析application/json类型的。由于源码是按照顺序遍历,所以必然会取到第6个。

我们可以看到内存地址是@6766,验证了我们的猜想。

继续跟踪代码,就发现了我们之前的报错:

因为这里的反序列化框架使用的是Jackson,而Jackson默认的日期格式是:yyyy-MM-dd'T'HH:mm:ss.SSSX

04 问题解决

问题原因知道了,解决也就简单了。

4.1 局部解决

我们需要使用Jackson的一个注解:@JsonForma

@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+08:00")

只需要在日期类型的字段上添加上面的注解,即可解决。一定要增加时区的修正,否则日期就会出现时差。

这种解决方案有其弊端。因为很多实体使用的是逆向工程,随时可能会被替换掉。还有就是这种作为需要为每一个日期字段增加注解。不能全局解决。

4.2 全局配置

Jackson框架有全局的配置,我们只需要修改全局配置即可解决。

spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
spring.jackson.time-zone=GMT+8

4.3 配置其他消息转化器

阿里开源的fajson或者fasjon2,本身默认就是yyyy-MM-dd HH:mm:ss日期格式。我们直接使用阿里的消息转化器器即可。

依赖引入

fasjon2作为第二代json处理工具,和第一代的引入方式有一些区别。这里需要引入fastjson2的扩展包,其中Spring的版本需要根据项目的版本引入。小编这里使用的是spring6

<dependency><groupId>com.alibaba.fastjson2</groupId><artifactId>fastjson2-extension-spring6</artifactId><version>2.0.45</version>
</dependency>

注册

@Configuration
public class BeanConfig implements WebMvcConfigurer {@Overridepublic void extendMessageConverters(List<HttpMessageConverter<?>> converters) {FastJsonConfig fastJsonConfig = new FastJsonConfig();// 这里可以按需指定日期格式
//        fastJsonConfig.setDateFormat("yyyy-MM-dd HH:mm:ss");FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();converter.setFastJsonConfig(fastJsonConfig);converters.add(0, converter);}
}

fastJsonConfig 作为消息转化器的配置项,在项目中按需配置即可。从之前的源码来看,消息转化器是按照顺序解析的,我们需要将消自定的FastJsonHttpMessageConverter加载在前面。或者直接用set的方式替换默认的消息转化器。

05 小结

@RequestBody 的解析是一个由 DispatcherServlet -> HandlerAdapter -> RequestResponseBodyMethodProcessor -> HttpMessageConverter 协同完成的复杂过程。理解这个流程对于深入掌握 Spring MVC、高效处理 RESTful API 以及精准定位和解决相关问题至关重要。它完美地体现了 Spring 框架通过组件分工和策略接口提供的强大扩展能力和灵活性。

理解其原理,解决方案也就明朗了。

http://www.dtcms.com/a/495249.html

相关文章:

  • 分布式事务以及Seata(XA、AT模式)
  • 做网站的 简历标识设计网
  • 平台网站建设意见征求表社区类网站开发
  • 电脑零配件行业MES系统:快速实现全过程信息溯源
  • 基于单片机与上位机的智能宠物喂食管理系统设计
  • 新奇特:黑猫警长的纳米世界,忆阻器与神经网络的智慧
  • 【深度学习新浪潮】LLM 大模型压缩落地实践(2025 版)
  • 神经网络之计算图repeat节点
  • 河北廊坊做网站珠海企业网站设计
  • 网站建设培训 ppt做网站有哪些
  • 【RK3588开发】RKNN库的使用
  • 使用 Python 语言 从 0 到 1 搭建完整 Web UI自动化测试学习系列 18--测试框架Pytest基础 2--插件和参数化
  • 玩具 网站模板成立一个网站
  • 阿里网站注册阿里云网站怎么建设
  • 【排查实录】Web 页面能打开,服务器能通接口,客户端却访问失败?原因全在这!
  • 【Linux】系统性能排查:解决卡顿问题
  • 建网站要注意的细节建免费的网站
  • 手机网站建设收费网站建设 合肥
  • Qwen3-0.6模型开关思考模式测试
  • FT62FC3X 8位MCU单片机选型表,详细解析FT62FC31A/32A/33A/35A/3FA
  • 鸿蒙NEXT Sensor Service Kit开发指南:解锁传感器数据的无限潜能
  • 开源项目:FlyCut Caption智能视频字幕裁剪工具
  • Fedora 16上源码建立pydev + eclipse的OpenStack开发环境笔记草稿
  • 便携式榨汁机方案开发,榨汁机果汁机MCU控制方案设计
  • 杭州如何做百度的网站网页是什么
  • 【软考备考】软件架构设计需要考虑系统性能 如何使用缓存提高系统性能知识点七
  • 南京做网站dmooo学校自己做的网站需要买服务器吗
  • 鸿蒙实现可以上下左右滑动的表格-摆脱大量ListScroller
  • 笔试强训:Week -2
  • webpack - 单独打包指定JS文件(因为不确定打出的前端包所访问的后端IP,需要对项目中IP配置文件单独拿出来,方便运维部署的时候对IP做修改)