I am trying to use bean validation in Webflux. This is what I have so far:
@PostMapping(\"contact\")
fun create(@RequestBody @Valid contact: Mono)
You need to also add bindingResult: BindingResult
to as an extra parameter. When the method begins you can do something like bindingResult.isValid()
. We use an aspect over all controller methods to return an error message with the validation errors to the user.
I had to combine few of the answers from SO to get the validation correct. What I did was:
Made my data class as suggested here.
javax.validation.Validator
class to validate the data
class.import javax.validation.Validator
import javax.validation.ConstraintViolationException
@Service
open class MyService {
@Autowired
lateinit var repository: UserRepository
@Autowired
lateinit var validator: Validator
open fun createUser(user: User): Mono<User> {
val violations = validator.validate(user)
//if it violates our constraints, it will throw an exception, which we
//handle using global exception handler
if(violations.isNotEmpty()){
throw ConstraintViolationException(violations)
}
return repo.save(user)
}
. . .
}
spring.mvc.throw-exception-if-no-handler-found=true spring.resources.add-mappings=false
And to catch the exceptions.
@RestControllerAdvice
open class ExceptionHandlers {
@ExceptionHandler(ConstraintViolationException::class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
fun throwConstraintViolationExcetion(ex: ConstraintViolationException): ApiResponse {
return ApiResponse (message= ex.message.toString(), data= null);
}
}
p.s ApiResponse
is just a data class that takes server message & data as parameters.
No need to handle errors in @Controller
as it will be thrown by the @Service
class
The annotations you have placed in the example project are actually annotations on the constructor parameters of the Ticket
class. For Spring validation, you need to annotate the fields instead. You can do this in Kotlin by using annotation use-site targets.
In this specific case, your Ticket class should look like this:
data class Ticket(
@field:Id
@field:JsonSerialize(using = ToStringSerializer::class)
val id: ObjectId = ObjectId.get(),
@field:Email
@field:Max(200)
@field:NotEmpty
val email: String,
@field:NotEmpty
@field:Size(min = 2, max = 200)
val name: String,
@field:NotEmpty
@field:Size(min = 10, max = 2000)
val message: String
)
This, along with the following controller function will work and return errors as expected:
@PostMapping("tickets")
fun create(@RequestBody @Valid contact: Mono<Ticket>) : Mono<Ticket> {
return contact.flatMap { ticketRepository.save(it) }
.doOnError{ Error("test") }
}