I'm making and simple application in Kotlin using Spring but I'm having a problem with the validation.
I have this entity class:
@Entity
@Table(name = "category")
data class Category(
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Long?,
@field:NotNull @field:NotEmpty val name: String)
And my controller function like this:
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
fun create(@Valid @RequestBody category: Category): ResponseEntity<Category>
create
have some code, but it is irrelevant for the question, my problem is with the request body validation. If I send a category with an empty name field, it is thrown a MethodArgumentNotValidException
exception, but if I send null to the field name
, the exception thrown HttpMessageNotReadableException
instead. Does anyone knows if it is possible to make passing null to a field marked with @NotNull
to also throw MethodArgumentNotValidException
in Kotlin.
So your problem is you specify the name field as not nullable, by default jackson module for kotlin will check it and throw HttpMessageNotReadableException
which cause by MissingKotlinParameterException
during json mapping process. If you mark name
filed as nullable json mapping will passed and get to the spring validation phase with @Valid
then we will get MethodArgumentNotValidException
@Entity
@Table(name = "category")
data class Category(
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Long?,
@field:NotNull @field:NotEmpty val name: String?)
You can handle this issue by providing HttpMessageNotReadableException
handler
and then checking if the underlying cause is MissingKotlinParameterException
.
After that, you can provide custom validation error. I'm using zalando-problem
, so syntax is a bit different from vanilla spring, but you get the idea:
@ExceptionHandler
override fun handleMessageNotReadableException(
exception: HttpMessageNotReadableException,
request: NativeWebRequest
): ResponseEntity<Problem> {
// workaround
val cause = exception.cause
if (cause is MissingKotlinParameterException) {
val violations = setOf(createMissingKotlinParameterViolation(cause))
return newConstraintViolationProblem(exception, violations, request)
}
return create(Status.BAD_REQUEST, UnableToReadInputMessageProblem(), request)
}
private fun createMissingKotlinParameterViolation(cause: MissingKotlinParameterException): Violation {
val name = cause.path.fold("") { jsonPath, ref ->
val suffix = when {
ref.index > -1 -> "[${ref.index}]"
else -> ".${ref.fieldName}"
}
(jsonPath + suffix).removePrefix(".")
}
return Violation(name, "must not be null")
}
This way you get get nice output with proper constraint error.
You may try to declare @ExceptionHandler
for MissingKotlinParameterException
directly (though I've tried, but it didn't some reason), but I can't guarantee it'll work.
Code samples for path parsing are taken from here
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With