Reputation: 978
I am trying to add validation to my data models in kotlin, The simple fields are easy to do using the @field
annotations. But, I am struggling to do the same with collections.
I have uploaded the issue to github here
The java model is working with no issues but the kotlin version is not. I am adding both the models here.
public class JavaUser {
@NotEmpty
@NotNull
@Pattern(regexp = "[a-z]*", message = "Only lower case first name")
private String name;
private List<
@NotNull
@NotEmpty
@Pattern(regexp = "\\d{10}", message = "Only 10 digits")
String> phones;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<String> getPhones() {
return phones;
}
public void setPhones(List<String> phones) {
this.phones = phones;
}
}
data class KotlinUser(
@field:NotEmpty
@field:NotNull
@field:Pattern(regexp = "[a-z]*", message = "Only lower case first name")
val name: String,
// Cannot use @field here, anything else we could use?
val phones: List<
@NotNull
@NotEmpty
@Pattern(regexp = "\\d{10}", message = "Only 10 digits")
String>
)
My tests - The java test passes but the kotlin one fails
@Test
fun `java user validation`() {
val javaUser = JavaUser()
javaUser.name = "sadfjsjdfhsjdf"
javaUser.phones = listOf("dfhgd")
webTestClient.put().uri("/user/java")
.body(BodyInserters.fromObject(javaUser))
.exchange()
.expectStatus().is4xxClientError
}
@Test
fun `kotlin user validation`() {
val kotlinUser = KotlinUser(name = "sadfjsjdfhsjdf", phones = listOf("dfhgd"))
webTestClient.put().uri("/user/kotlin")
.body(BodyInserters.fromObject(kotlinUser))
.exchange()
.expectStatus().is4xxClientError
}
Controller
@RestController
class Controller {
@PutMapping("/user/java")
fun putUser(@RequestBody @Valid javaUser: JavaUser): Mono<ResponseEntity<String>> =
Mono.just(ResponseEntity("shouldn't get this", HttpStatus.OK))
@PutMapping("/user/kotlin")
fun putUser(@RequestBody @Valid kotlinUser: KotlinUser): Mono<ResponseEntity<String>> =
Mono.just(ResponseEntity("shouldn't get this", HttpStatus.OK))
}
Any help would be greatly appreciated. Thank you!
Upvotes: 13
Views: 19057
Reputation: 3391
Simply use an init method. It runs after the constructor.
Example:
import java.time.LocalDateTime
data class ItemWithValidateCode(
var key: String,
var value: String,
val validFrom: LocalDateTime,
val validUntil: LocalDateTime,
) {
init {
require(validFrom == null || validUntil == null || validFrom.isBefore(validUntil)) { "validFrom must be before validUntil" }
}
}
Upvotes: -1
Reputation: 4599
A workaround there is to use the @Validated
from spring in your controller.
Example:
@RestController
@RequestMapping("/path")
@Validated
class ExampleController {
@PostMapping
fun registerReturns(@RequestBody @Valid request: List<@Valid RequestClass>) {
println(request)
}
}
RequestClass:
@JsonIgnoreProperties(ignoreUnknown = true)
data class RequestClass(
@field:NotBlank
val field1: String?,
@field:NotBlank
@field:NotNull
val field2: String?)
This way Beans Validation is applied.
Upvotes: 0
Reputation: 41
Try this:
data class KotlinUser(
@field:NotEmpty
@field:NotNull
@field:Pattern(regexp = "[a-z]*", message = "Only lower case first name")
val name: String,
@field:Valid
@field:NotEmpty
val phones: List<Phone>
)
data class Phone(
@NotBlank
@Pattern(regexp = "\\d{10}", message = "Only 10 digits")
val phoneNumber: String?
)
Upvotes: 4
Reputation: 99
If you don't stick to Bean Validation, YAVI can be an alternative.
You can define the validation as follows:
import am.ik.yavi.builder.ValidatorBuilder
import am.ik.yavi.builder.forEach
import am.ik.yavi.builder.konstraint
data class KotlinUser(val name: String,
val phones: List<String>) {
companion object {
val validator = ValidatorBuilder.of<KotlinUser>()
.konstraint(KotlinUser::name) {
notEmpty()
.notNull()
.pattern("[a-z]*").message("Only lower case first name")
}
.forEach(KotlinUser::phones, {
constraint(String::toString, "value") {
it.notNull().notEmpty().pattern("\\d{10}").message("Only 10 digits")
}
})
.build()
}
}
Here is a YAVI version of your code.
Upvotes: -1
Reputation: 12184
This is currently not supported. The Kotlin compiler currently ignores annotations on types.
See for details:
There are also issues on the Kotlin issue tracker for this:
The latter has target version 1.4
.
Upvotes: 9