Kotlin 需要 NotNull 吗?
Is NotNull needed on Kotlin?
我有一个class:
class User(
var name: String
)
和映射的 post 请求:
@PostMapping("/user")
fun test(@Valid @RequestBody user: User) {
//...
}
如果客户端将发送 name: null
用户的 JSON 怎么办?会不会被 MVC Validator 拒绝或者抛出异常?我应该用 @NotNull
注释 name
吗?不幸的是,我无法检查,因为只能编写测试(无法创建 User(null)
)。
因为 name
是 User
的 not-null 参数,它永远不能接受 null
s。
保证Kotlin编译器:
在 User
构造函数中插入一个 null
检查,如果某些 Java 代码试图传递 null
.
用@NotNull
注释name
以便与Java API
保持一致
不添加任何 @NotNull
注释,因为没有一个是每个人都同意的:(
下面是对应的docs
更新
我重新检查了一下,Kotlin 编译器 v1.0.6 确实 从 org.jetbrains.annotations
插入 @Nullable
和 @NotNull
。我已经相应地更新了答案。
您可以避免使用 @NotNull
,因为它永远不会被 non-nullable 属性 触发(bean 验证仅在 Jackson 反序列化 JSON 之后才开始 - 这这是失败的地方)。
假设您使用 jackson-module-kotlin, then you should get an exception with MissingKotlinParameterException
作为根本原因,然后您可以在 @ControllerAdvice
中处理它。在您的建议中,您可以处理正常的 bean 验证异常(例如 ConstraintViolationException
由 @Size
引起)和缺少 non-null 构造函数参数(MissingKotlinParameterException
)和 return 一个 API 指示错误字段的响应。
唯一需要注意的是 jackson-kotlin-module
fails on the first missing property,与 Java 中的 bean 验证不同,它将 return 所有违规行为。
根据我的测试,@NotNull
根本不会影响 MVC 验证。您只会在控制台中收到 WARN
消息,这是正常的:
Failed to read HTTP message: org.springframework.http.converter.HttpMessageNotReadableException: Could not read document: Instantiation of...
我现在的解决方法(将文件放在任何地方):
@Order(Ordered.HIGHEST_PRECEDENCE)
@ControllerAdvice
class ExceptionHandler {
@ResponseStatus(BAD_REQUEST)
@ResponseBody
@ExceptionHandler(MissingKotlinParameterException::class)
fun missingKotlinParameterException(ex: MissingKotlinParameterException): Error? {
return createMissingKotlinParameterViolation(ex)
}
private fun createMissingKotlinParameterViolation(ex: MissingKotlinParameterException): Error{
val error = Error(BAD_REQUEST.value(), "validation error")
val errorFieldRegex = Regex("\.([^.]*)\[\\"(.*)\"\]$")
val errorMatch = errorFieldRegex.find(ex.path[0].description)!!
val (objectName, field) = errorMatch.destructured
error.addFieldError(objectName.decapitalize(), field, "must not be null")
return error
}
data class Error(val status: Int, val message: String, val fieldErrors: MutableList<CustomFieldError> = mutableListOf()) {
fun addFieldError(objectName: String, field: String, message: String) {
val error = CustomFieldError(objectName, field, message)
fieldErrors.add(error)
}
}
data class CustomFieldError(val objectName: String, val field: String, val message: String)
我使用以下异常处理程序:
@Order(Ordered.HIGHEST_PRECEDENCE)
@ControllerAdvice
class ExceptionHandlerResolver {
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ResponseBody
@ExceptionHandler(MissingKotlinParameterException::class)
fun missingKotlinParameterException(ex: MissingKotlinParameterException): MyCustomError? {
return MyCustomError(
timestamp = LocalDateTime.now(),
status = HttpStatus.BAD_REQUEST,
exception = ex,
validationMessages = listOf(
ValidationMessage(
field = ex.path.fold("") { result, segment ->
if (segment.fieldName != null && result.isEmpty()) segment.fieldName
else if (segment.fieldName != null) "$result.${segment.fieldName}"
else "$result[${segment.index}]"
},
message = "value is required"
)
)
)
}
}
它支持任何 Json 路径的异常,例如:arrayField[1].arrayItemField.childField
MyCustomError 和 ValidationMessage 类:
data class MyCustomError(
val timestamp: LocalDateTime,
@JsonIgnore val status: HttpStatus,
@JsonIgnore val exception: Exception? = null,
val validationMessages: List<ValidationMessage>? = null
) {
@JsonProperty("status") val statusCode = status.value()
@JsonProperty("error") val statusReasonPhrase = status.reasonPhrase
@JsonProperty("exception") val exceptionClass = exception?.javaClass?.name
@JsonProperty("message") val exceptionMessage = exception?.message
}
data class ValidationMessage(val field: String, val message: String)
我有一个class:
class User(
var name: String
)
和映射的 post 请求:
@PostMapping("/user")
fun test(@Valid @RequestBody user: User) {
//...
}
如果客户端将发送 name: null
用户的 JSON 怎么办?会不会被 MVC Validator 拒绝或者抛出异常?我应该用 @NotNull
注释 name
吗?不幸的是,我无法检查,因为只能编写测试(无法创建 User(null)
)。
因为 name
是 User
的 not-null 参数,它永远不能接受 null
s。
保证Kotlin编译器:
在
User
构造函数中插入一个null
检查,如果某些 Java 代码试图传递null
.用
@NotNull
注释name
以便与Java API 保持一致
不添加任何@NotNull
注释,因为没有一个是每个人都同意的:(
下面是对应的docs
更新
我重新检查了一下,Kotlin 编译器 v1.0.6 确实 从 org.jetbrains.annotations
插入 @Nullable
和 @NotNull
。我已经相应地更新了答案。
您可以避免使用 @NotNull
,因为它永远不会被 non-nullable 属性 触发(bean 验证仅在 Jackson 反序列化 JSON 之后才开始 - 这这是失败的地方)。
假设您使用 jackson-module-kotlin, then you should get an exception with MissingKotlinParameterException
作为根本原因,然后您可以在 @ControllerAdvice
中处理它。在您的建议中,您可以处理正常的 bean 验证异常(例如 ConstraintViolationException
由 @Size
引起)和缺少 non-null 构造函数参数(MissingKotlinParameterException
)和 return 一个 API 指示错误字段的响应。
唯一需要注意的是 jackson-kotlin-module
fails on the first missing property,与 Java 中的 bean 验证不同,它将 return 所有违规行为。
根据我的测试,@NotNull
根本不会影响 MVC 验证。您只会在控制台中收到 WARN
消息,这是正常的:
Failed to read HTTP message: org.springframework.http.converter.HttpMessageNotReadableException: Could not read document: Instantiation of...
我现在的解决方法(将文件放在任何地方):
@Order(Ordered.HIGHEST_PRECEDENCE)
@ControllerAdvice
class ExceptionHandler {
@ResponseStatus(BAD_REQUEST)
@ResponseBody
@ExceptionHandler(MissingKotlinParameterException::class)
fun missingKotlinParameterException(ex: MissingKotlinParameterException): Error? {
return createMissingKotlinParameterViolation(ex)
}
private fun createMissingKotlinParameterViolation(ex: MissingKotlinParameterException): Error{
val error = Error(BAD_REQUEST.value(), "validation error")
val errorFieldRegex = Regex("\.([^.]*)\[\\"(.*)\"\]$")
val errorMatch = errorFieldRegex.find(ex.path[0].description)!!
val (objectName, field) = errorMatch.destructured
error.addFieldError(objectName.decapitalize(), field, "must not be null")
return error
}
data class Error(val status: Int, val message: String, val fieldErrors: MutableList<CustomFieldError> = mutableListOf()) {
fun addFieldError(objectName: String, field: String, message: String) {
val error = CustomFieldError(objectName, field, message)
fieldErrors.add(error)
}
}
data class CustomFieldError(val objectName: String, val field: String, val message: String)
我使用以下异常处理程序:
@Order(Ordered.HIGHEST_PRECEDENCE)
@ControllerAdvice
class ExceptionHandlerResolver {
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ResponseBody
@ExceptionHandler(MissingKotlinParameterException::class)
fun missingKotlinParameterException(ex: MissingKotlinParameterException): MyCustomError? {
return MyCustomError(
timestamp = LocalDateTime.now(),
status = HttpStatus.BAD_REQUEST,
exception = ex,
validationMessages = listOf(
ValidationMessage(
field = ex.path.fold("") { result, segment ->
if (segment.fieldName != null && result.isEmpty()) segment.fieldName
else if (segment.fieldName != null) "$result.${segment.fieldName}"
else "$result[${segment.index}]"
},
message = "value is required"
)
)
)
}
}
它支持任何 Json 路径的异常,例如:arrayField[1].arrayItemField.childField
MyCustomError 和 ValidationMessage 类:
data class MyCustomError(
val timestamp: LocalDateTime,
@JsonIgnore val status: HttpStatus,
@JsonIgnore val exception: Exception? = null,
val validationMessages: List<ValidationMessage>? = null
) {
@JsonProperty("status") val statusCode = status.value()
@JsonProperty("error") val statusReasonPhrase = status.reasonPhrase
@JsonProperty("exception") val exceptionClass = exception?.javaClass?.name
@JsonProperty("message") val exceptionMessage = exception?.message
}
data class ValidationMessage(val field: String, val message: String)