日期格式化时注解@DateTimeFormat无效的问题分析
日期格式化時(shí)注解@DateTimeFormat無效的問題分析
背景
有時(shí)候我們在寫接口時(shí),需要把前臺傳來的日期String類型轉(zhuǎn)為Date類型
這時(shí)我們可能會用到@DateTimeFormat注解
在請求數(shù)據(jù)為非JSON格式時(shí),這個(gè)注解是沒有問題的,可用的;
但是當(dāng)請求數(shù)據(jù)為JSON格式時(shí),問題就出現(xiàn)了
- 此時(shí)如果請求參數(shù)沒有加@RequestBody注解,那么請求參數(shù)不會執(zhí)行類型轉(zhuǎn)換操作,數(shù)據(jù)都是默認(rèn)為空(基本類型比如int = 0, 對象引用比如Date date= null)
- 此時(shí)如果請求參數(shù)有加@RequestBody注解,那么請求參數(shù)會執(zhí)行JSON類型轉(zhuǎn)換操作,但是轉(zhuǎn)換會提示異常
所以文章題目中所說的有時(shí)無效,指的就是上面這兩種情況
目錄
本文分三步走,如下所示,其中會穿插著介紹@DateTimeFormat、@RequestBody、@JsonFormat注解
分析
1. 基礎(chǔ)代碼:
AnnationApplication.java:主程序兼控制器
package com.jalon.annation;import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController;@SpringBootApplication @RestController public class AnnationApplication {public static void main(String[] args) {SpringApplication.run(AnnationApplication.class, args);}@PostMapping("/personPost")public Person personPost(Person person){System.out.println(person);return person;} }Person.java 實(shí)體類
package com.jalon.annation;import com.fasterxml.jackson.annotation.JacksonAnnotation; import org.springframework.format.annotation.DateTimeFormat; import org.springframework.stereotype.Component;import java.util.Date;public class Person {private int age;@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")private Date birth;@Overridepublic String toString() {return "Person{" +"age=" + age +", birth=" + birth +'}';}// 省略getter/setter }2. 案例分析:
這里我們用的是PostMan進(jìn)行測試,請求示例如下
所有示例全程都有@DateTimeFormat注解
示例1:
-
請求方式:Post請求
-
數(shù)據(jù)格式:非JSON格式,比如form-data
-
請求資源:personPost(Person person),無@RequestBody注解
具體請求內(nèi)容和返回結(jié)果如下所示
可以看到,前臺返回正常(數(shù)據(jù)無誤),說明@DateTimeFormat有效,成功解析了日期字符串
這里返回的數(shù)據(jù)都是經(jīng)過@ResponseBody處理過的,因?yàn)槲覀儧]有配置返回?cái)?shù)據(jù)的日期格式化,所以這里返回的日期格式是默認(rèn)的
@ResponseBody對應(yīng)于@RequestBody;
- 前者負(fù)責(zé)將Java對象序列號成JSON數(shù)據(jù)進(jìn)行返回
- 后者負(fù)責(zé)解析請求過來的JSON數(shù)據(jù),解析成對應(yīng)的Java對象
我們再來看下后臺,打印如下:
Person{age=1, birth=Wed Jan 01 00:00:00 CST 2020}可以看到,后臺打印正常(數(shù)據(jù)無誤,日期格式忽略,因?yàn)檫@里的date.toString用的Date的默認(rèn)方法)
從上面的結(jié)果我們可以看到,@DateTimeFormat只是負(fù)責(zé)解析傳來的日期字符串,轉(zhuǎn)為對應(yīng)的日期對象;
但是并不會修改原有的日期對象的格式(從前臺返回和后臺輸出可以看到,日期格式不受@DateTimeFormat的影響)
示例2:
-
請求方式:Post請求
-
數(shù)據(jù)格式:JSON格式,比如application/json
-
請求資源:personPost(Person person),無@RequestBody注解
具體請求內(nèi)容和返回結(jié)果如下所示
可以看到,返回?cái)?shù)據(jù)都為空(默認(rèn)的初始值),說明數(shù)據(jù)都沒有傳過去,不止是date,連基本類型int都沒過去
我們再來看下后臺,打印如下
Person{age=0, birth=null} // 跟前臺返回的數(shù)據(jù)一致可以看到,后臺解析到的數(shù)據(jù)也是空的,所以上面返回的當(dāng)然是空的
原因就是默認(rèn)的類型轉(zhuǎn)換器是沒有轉(zhuǎn)化成JSON格式的對應(yīng)轉(zhuǎn)換類的,部分轉(zhuǎn)換器如下所示,(core.convert.support包)
解決:所以這里對應(yīng)的解決辦法就是,自己創(chuàng)建一個(gè)JSON轉(zhuǎn)換器
但是實(shí)際上這個(gè)已經(jīng)有實(shí)現(xiàn)了,只是沒有觸發(fā),如下所示的構(gòu)建工具(http.converter.json包),就是用來配置相關(guān)的json序列化和反序列化的
現(xiàn)在我們可以通過@RequestBody注解來觸發(fā),它在接收到JSON格式的數(shù)據(jù)時(shí),會自動調(diào)用對應(yīng)的JSON轉(zhuǎn)換器
下面的示例3就是這個(gè)例子
加了@RequestBody后,默認(rèn)只接受application/json格式的數(shù)據(jù),如果傳入其他格式,會報(bào)415不支持的類型
示例3:
-
請求方式:Post請求
-
數(shù)據(jù)格式:JSON格式,比如application/json
-
請求資源:personPost(@RequestBody Person person),有@RequestBody注解
具體請求內(nèi)容和返回結(jié)果如下所示
可以看到,報(bào)錯了,提示400,這種一般屬于客戶端錯誤(比如數(shù)據(jù)格式不正確,數(shù)據(jù)過大等)
我們再來看下后臺,打印如下
2021-05-15 13:48:41.578 WARN 38426 --- [nio-8080-exec-2] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize value of type `java.util.Date` from String "2020-01-01 00:00:00": not a valid representation (error: Failed to parse Date value '2020-01-01 00:00:00': Cannot parse date "2020- 01-01 00:00:00": while it seems to fit format 'yyyy-MM-dd'T'HH:mm:ss.SSSX', parsing fails (leniency? null)); nested exception is com.fasterxml.jackson.databind.exc.InvalidFormatException:Cannot deserialize value of type `java.util.Date` from String "2020-01-01 00:00:00": not a valid representation (error: Failed to parse Date value '2020-01-01 00:00:00': Cannot parse date "2020-01-01 00:00:00": while it seems to fit format 'yyyy-MM-dd'T'HH:mm:ss.SSSX', parsing fails (leniency? null))at [Source: (PushbackInputStream); line: 3, column: 14] (through reference chain: com.jalon.annation.Person["birth"])]這里我們提取關(guān)鍵的部分來看:
1. nested exception is com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type `java.util.Date` from String "2020-01-01 00:00:00"2. Cannot parse date "2020-01-01 00:00:00": while it seems to fit format 'yyyy-MM-dd'T'HH:mm:ss.SSSX'首先這里跟示例2不同,這里起碼做了嘗試轉(zhuǎn)換,只是沒有找到對應(yīng)的格式,所以轉(zhuǎn)換失敗了
可以看到,它并沒有按照上面我們的@DateTimeFormat注解去解析,而是按照’'yyyy-MM-dd’T’HH:mm:ss.SSSX"這個(gè)格式去解析
這里如果想投機(jī)的話,可以在前臺直接傳入’'yyyy-MM-dd’T’HH:mm:ss.SSSX’格式的數(shù)據(jù),如下:
但是這種辦法對于前端很不友好(極其不好)
所以下面還是給出正常的解決辦法
解決:所以這里的解決辦法就是自己定義日期格式
- 方案一:局部注解來解決,比如在date字段添加@JsonFormat()注解
局部的特點(diǎn):靈活,但是配置繁瑣,不統(tǒng)一(每個(gè)字段都要加)
- 方案二:全局配置來解決,比如配置一個(gè)Jackson2ObjectMapperBuilderCustomizer,然后自定義日期反序列化格式
全局的特點(diǎn):不靈活,但是直觀清晰,配置統(tǒng)一
3. 結(jié)論分析:
主要根據(jù)請求的數(shù)據(jù)類型來對比
- 請求非JSON數(shù)據(jù),建議用@DateTimeFormat即可(比如get請求,當(dāng)然get請求也可以請求JSON數(shù)據(jù),只是不推薦)
- 請求JSON數(shù)據(jù),建議用@ReqeustBody來轉(zhuǎn)換數(shù)據(jù),然后搭配局部注解@JsonFormat或者全局配置來修改默認(rèn)的日期解析格式(默認(rèn)"yyyy-MM-dd’T’HH:mm:ss.SSSX")
總結(jié)
注解相關(guān):
日期格式化相關(guān):
參考內(nèi)容:
- @RequestBody: https://blog.csdn.net/justry_deng/article/details/80972817/
- @DateTimeFormat: https://segmentfault.com/a/1190000020423352
后記
學(xué)習(xí)之路漫漫,共勉之
寫在最后:
愿你的意中人亦是中意你之人
總結(jié)
以上是生活随笔為你收集整理的日期格式化时注解@DateTimeFormat无效的问题分析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: AI理论知识整理(18)-内积与范数
- 下一篇: 你知道Java中final和static