Kotlin + Spring Boot JSON 中 Int 字段的默认值

问题描述 投票:0回答:4

我正在使用 Kotlin + Spring Boot + Hibernate 编写简单的 CRUD 并提出了这个问题:

我在

User
类中有一个名为
age
的字段,并输入
Int

import javax.persistence.*
import javax.validation.constraints.NotBlank
import javax.validation.constraints.Size

@Entity
@Table(name = "users")
data class User(
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        val id: Long,

        @NotBlank
        @Column(unique = true)
        @Size(min = 1, max = 100)
        val username: String,

        @NotBlank
        @Size(max = 50)
        val firstName: String,

        @Size(max = 50)
        val lastName: String?,

        @Size(min = 1, max = 100)
        val age: Int
)

然后我有创建用户的发布请求

@PostMapping
fun post(@RequestBody user: User): User {
    log.debug("POST: {}", user)
    return userRepository.save(user)
}

问题是,当我错过 JSON 中的

age
字段时,它不会响应 400 错误,而是默认为
0

如何将此字段设为必填?

spring kotlin
4个回答
2
投票

我认为一个更大的问题是您将同一个类(您的实体)用于多种目的。这使得此类非常不灵活,因为您无法使其适应 REST 端点的需求以及 Hibernate 的需求。

我会做的是在两者之间划定界限(建议像Clean Architecture那样做):

data class UserDTO(val id: Long?,
        var username: String?,
        var firstName: String?,
        var lastName: String?,
        var age: Int?) {

    fun toUser() = User(
        id = id ?: signalError(),
        username = username ?: signalError(),
        firstName = firstName ?: signalError(),
        lastName = lastName ?: signalError(),
        age = age ?: signalError())

    private fun signalError() : Nothing = throw IllegalArgumentException(
        "Missing parameter in UserDTO.")

}

1
投票

由于这篇post,我解决了这个问题, 还要感谢 @Adam Arold 展示最佳实践。

我创建了新的

UserDTO
类:

import ai.leavingstone.flywayspringdemo.domain.User
import javax.validation.constraints.*

data class UserDTO(
        private val id: Long = -1,

        @field:NotBlank
        @field:Size(min = 1, max = 100)
        private val username: String,

        @field:NotBlank
        @field:Size(max = 50)
        private val firstName: String,

        @field:Size(max = 50)
        private val lastName: String?,

        @field:NotNull
        private val age: Int?
) {

    fun toUser(): User = User(
            id,
            username,
            firstName,
            lastName,
            age!!
    )

}

在所有数据验证注释都不起作用之前,添加

@field
前缀后,它可以正常工作。
@NotNull
上的
Int
也可以正常工作,并且也是
id
字段的默认值。


0
投票

您应该使用

@Valid @RequestBody user: User
作为方法参数,以便 spring 将验证给定的主体。这应该是默认完成的,但以防万一,把它写下来,这对新工人等来说更好。

此外@

Size
注释不支持基元或数字变量。它只是验证
String
Array
Map
List
...的长度...所以只需用
@Min
@Max
进行注释即可。所以默认值 0 将不再有效。

另外,对于可空对象进行验证,您可以使用

Integer
而不是
Int
,这样如果您希望负数或 0 也有效,则可以仅检查
@NotNull
,而无需获取默认值 0。


0
投票

您遇到的问题的发生是因为 Kotlin 中的

Int
被编译为 Java 中的原始类型
int
。反序列化时,像
int
这样的原始类型不能被赋值为
null
。相反,它们会自动使用默认值进行初始化,在
int
的情况下为
0
。此行为与 Java 处理基本类型的方式一致。

Java中其他基本类型的默认值如下:

  • int = 0
  • byte = 0
  • char = ('\u0000')
  • short = 0
  • long = 0
  • float = 0.0
  • double = 0.0
  • boolean = false

与 JSR 303(Bean Validation)或 JSR 380(Bean Validation 2.0)等验证框架交互时,这可能会导致意外行为。例如,像

Int
这样的字段在反序列化期间永远不会是
null
,因此
@NotNull
验证可能无法按预期工作,因为该字段将被分配默认值
0
而不是
null

如何解决

现在我们了解了根本原因,有两种简单的方法可以解决此问题:

  1. 将字段更改为可空类型: 将

    Int
    等字段转换为可空类型,例如
    Int?
    。这样,Kotlin 会将它们编译成相应的包装类型(Java 中的
    Integer
    ),并且
    @NotNull
    将按预期工作。然而,这种方法引入了不必要的字段可空性检查,理想情况下这些字段永远不应该是
    null

    data class Student(
        @field:NotNull
        val id: Int?,  // Nullable type
        ...
    )
    

    这将确保该字段在反序列化期间确实可以是

    null
    ,并且如果输入中缺少该字段,
    @NotNull
    将正确触发验证。

  2. 使用

    @Min
    @Max
    进行验证
    : 另一种选择是使用
    @Min
    @Max
    等注释来验证字段,尽管这种方法可能感觉违反直觉。您可以将
    0
    值视为无效或“空等效”输入并相应地对其进行验证。

    例如,如果

    0
    不是您字段的有效值,您可以添加验证约束来拒绝它:

    data class Student(
        @field:Min(1, message = "Value must be greater than 0")
        val id: Int,  // Non-nullable type
        ...
    )
    

    这样,即使该字段自动初始化为

    0
    ,您也可以将
    0
    视为无效值并像处理
    null
    一样处理它。

© www.soinside.com 2019 - 2024. All rights reserved.