enum的反序列化问题 415 Unsupported Media Type
当你的程序在线上出现了error code 415,而本地环境正常的时候,可能就是由于enum的反序列化导致的。
当在enum的反序列化工厂方法上添加了@JsonCreator,且该方法有多个参数,每个参数前都加了@JsonProperty注解,但没有在JsonProperty中显式的指定value为参数名时,就会导致这一问题。
这是一个神奇的bug,在本地测试的时候,不管是用postman还是前端页面发送参数,后端的controller都可以正常的接收,将json的字符串,反序列化为实体类。但是一到了线上,就显示415 content type not supported
{
"timestamp": "2021-08-13T07:29:27.173+0000",
"status": 415,
"error": "Unsupported Media Type",
"message": "Content type 'application/json;charset=UTF-8' not supported",
"path": "/ops/submit_inventory_evaluate"
}
经检查restcontroller requestmapping requestbody注解都放在了合适位置,且前端js里content type也采用了application/json,或postman在header里content type也是如此设置。最初怀疑为前端的问题,反复检查无误后,通过postman测试,发现还是415错误,之后将问题定位到了后端。
因为本地环境正常,导致测试很困难,只能一点点的排查,发到线上环境再测试,最终将问题锁定到了实体类中一个enum属性。在加了@JsonIgnore注解后,线上环境就可以正常的反序列化了,在查看enum中的toEnum方法时,注意到了上面的@JsonCreator注解,仔细阅读jackson2.9.0中关于该注解的说明。发现该注解当作用于有多个参数的构造或工厂方法上时,每个参数都需要加@JsonProperty,同时在该注解中需要显式的指定value的值,对应哪个属性。除非你使用了支持检测参数名称的扩展模块,因为JDK8之前,默认 JDK 版本无法从字节码中存储或检索参数名称。
通过这段话,我开始猜测是本地Oracle的JDK和线上JDK的区别,但是都是JDK8应该都支持这一特性。后面通过搜索这一特性——反射获取方法的参数名称,发现除了要求JDK8及以上,还有一点是编译的时候必须指定编译选项:-parameters,来打开这一特性的支持,默认是关闭的状态。
打开这一特性有三种方法:
- 手动命令方式编译:javac -parameters XXX.java
- IDE(以Idea为例)编译:Settings -> Build,Execution,Deployment -> Compiler -> Java Compiler
- Additional command line parameters框中设置为-parameters
- Override compiler parameters per-module中新增一个module并设置 Compilation options值为-parameters
- Maven编译:通过编译插件指定,保证项目迁移的正确性(推荐)
其中第二种方法是IDEA,默认开启的,通过删除这一参数设置,测试发现本地也出现了415错误,最终确认了问题的来源。