Spring(kotlin)控制器接收数据类的不可为空的参数为空

Posted

技术标签:

【中文标题】Spring(kotlin)控制器接收数据类的不可为空的参数为空【英文标题】:Spring (kotlin) controller receives non-nullable parameters of a data class as null 【发布时间】:2019-04-09 08:21:14 【问题描述】:

我遇到了一个我不知道如何处理的意外错误。

我有一个这样的数据类:

data class Payload (
  @SerializedName("id")
  var id: String
  @SerializedName("type")
  var type: String,
  @SerializedName("data")
  var data: String
)

还有一个像这样的简单 Spring 控制器:

@PostMapping("/some-endpoint")
fun dataHandler(@RequestBody payload: Payload): String
    when (payload.type)
        "someType" -> 
            val result = try 
                gson.fromJson(payload.data, payloadData::class.java)
             catch (e: Exception)
                throw BadDataException("Bad Data")
            
            payloadProcessor.process(result, payload.id) // NPE here
        
        "otherType" -> 
            doSomethingElseHere()
        
    

当执行到达 payloadProcessor.process 时,会发生空指针异常,因为 id 显然为空。另一方面,对象已创建,其余两个值似乎已正确填充。 如果我添加一个检查空值的 if 语句,ide 会抱怨负载属性永远不会为空,将 if 语句标记为冗余,但实际上并非如此。 我的印象是 null 安全属性是……嗯…… null 安全的。至少我希望在构造对象后会发生空指针异常。

我的问题是:

Spring 如何创建这样的对象 为什么在对象构造时没有检测到这一点 “Kotlin”的处理方式是什么,因为我不想在一个假定为 null 的安全对象中检查 null 参数。

【问题讨论】:

【参考方案1】:

问题在于 Gson,而不是 Spring。 Gson 是为与 Java 一起使用而设计的,Java 没有*可空类型和不可空类型之间的区别,因此 Gson 无论如何都会很乐意分配一个空值。

不幸的是,这里没有神奇的解决方案。您可以编写自己的TypeAdapter,它在构建数据类之前显式检查空值,但如果您有很多类,那将变得非常重复。或者,您可以创建自己的 TypeAdapterFactory 并使用 Kotlin 的反射库来检查哪些类型可以为空,哪些不能,但这会对性能和依赖关系产生各种影响。

* 有可空性注解(@Nonnull、@Nullable),但 Gson 不检查它们。

【讨论】:

感谢您的意见。我考虑过这一点,但在我的情况下,nullPointerException 不在 gson.fromJson() 生成的对象上,而是在接收到的对象本身的属性上。事实上,gson 的工作是作为 payload.data 不为空。但是 payload.id 是。 是的,当 Gson 反序列化您的数据时,它会将 null 分配给 result.id 字段,因为在 Java 中 String 可以为空。在您尝试使用result.id 的值之前不会出现错误,因为它是null 为了清楚起见,我说的是@RequestBody payload: Payload,而不是result = gson.fromJson(payload.data, payloadData::class.java),gson 正在转换数据属性whish 分配给结果。我不是 gson 专家,但 gson 如何影响有效载荷?该对象已经构建,仅由 gson 用于创建 payloadData 对象。我没有对有效载荷本身进行任何修改,只是读取它的属性。 啊,我明白了。我假设 payload 正在从网络请求中反序列化,它是否可以在内部使用 Gson 进行反序列化并在那里使用 null 值? spring内部使用gson吗?还是它使用类似的反序列化机制?这也是我的同事们提出的建议,也是我所怀疑的,但我真的很想有一个具体的理由,以便我将来避免类似的陷阱。我已经编写了自己的 typeAdapter,到目前为止它似乎表现还不错。因此,如果您可以请更新您的回复,以便我可以接受答案。

以上是关于Spring(kotlin)控制器接收数据类的不可为空的参数为空的主要内容,如果未能解决你的问题,请参考以下文章

错误:惰性类的 Getter 不能是最终的 Kotlin Spring Boot

Kotlin 初学者类的修饰符

使用 spring boot、kotlin 和 junit 进行休息控制器单元测试

Spring Kotlin @ConfigurationProperties 用于依赖项中定义的数据类

Kotlin和Spring Boot请求身体验证

Kotlin Spring Boot bean 验证不起作用