Controller validation in Kotlin Spring Boot - spring

I have a RestController with one endpoint. That endpoint accepts object of data class. Data class has 2 attributes. How do I make sure these attributes are validated?
My data class:
data class FormObject(val email: String, val age: Int)
And controller:
#PostMapping("submit")
fun submit(#RequestBody formObject: FormObject): FormObject {
return formObject
}
How do I make sure email is email and age is not bigger than 150?
Thanks,

You can use the Bean Validation Framework for this.
1) Annotate the request object as needing validation:
fun submit(#Valid #RequestBody formObject: FormObject): FormObject
^^^^^^
2) Annotate the fields of your data class with the appropriate validation annotations:
data class FormObject(
#field:NotBlank
val email: String,
#field:Min(1)
#field:Max(150)
val age: Int
)
Note that you have to apply the annotation to the field (not the parameter) or the validation won't happen the way we want. Also, if we define age as an Int, it will have a default value (0) if the caller does not send it, so I applied a the minimum validation on that to offset that (assuming age 0 is not ok, YMMV).

Related

Spring Boot Validation - Request parameter

I'm having an issue with Spring Boot validation on a REST controller. I'm using validation 2.4.1.
I'm trying to validate elements of a list. Here's an example:
#Validated
#RestController
#RequestMapping("path")
class ControllerClass(
private val service: ServiceClass
) {
#GetMapping()
suspend fun controllerMethod(
request: ServerHttpRequest,
#RequestParam(required = false)
#NotEmpty
id: List<#Positive Int>? // Here's the issue.
): ResponseEntity<Map<String, Any>> {
// Content...
}
}
#NotEmpty works properly on the list object, so when the parameter is empty (example.com/path?id=), the client receives a validation error message. However, #Positive doesn't work on the Int objects inside the list. I did some research and some people say it works. I also check the #Positive interface and it supposed to work like that.
I also tried using #Valid everywhere, with the type use, parameter and method, but it doesn't work either.
Is there anything else I have to do? For example, I know that, to use validation on data classes in Kotlin, you have to use #get: (#get:Positive, for example) for validation to work.
Thank you!

Required Int and Boolean values in API payload

I'm using Spring and Kotlin to create REST API. I have sample endpoint that looks like this
data class TestData(
#field:NotNull
val intValue: Int,
#field:NotNull
val booleanValue: Boolean
)
#RestController
#RequestMapping("/test")
class TestController {
#PostMapping
fun test(#RequestBody test: TestData) {
println(test)
}
}
And the issue is, I can send empty body in payload and in controller I'll recieve default values for Int and Boolean - TestData(intValue=0, booleanValue=false). How can I avoid that? Both 0 and false are valid values if provided by user, but I want him to explicitly pass them in request. If they are missing, I'd like to receive 400 bad request

Spring request param enum validation

I'm having my #RestController with service endpoint which takes enum as the parameter. This enum has 4 values but I would like to constraint the user to choose only from two of them. Something like
#RequestParam #Min(value= 1, message = "lorem ")
#Max(value = 10, message = "yfufhu")
but for enum - validating if given param is in {value1,value2}
Is there anyway to do it?
thanks!
To validate the path variable , the controller class should be annotated with #Validated.
#RestController
#Validated
public Class RestController(){
//...code
}
check this one
Hope it helps!!

Impact of #JsonTypeInfo in REST endpoints when concrete implementations are used

I'm wondering about the effects of adding a #JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "#class") to an interface.
My use case is that I have an interface Message with many subtypes. I want to be able to deserialize and serialize lists of messages in one endpoint.
My classes:
#JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "#class")
interface Message: Serializable
data class Message1(
val name: String,
val age: Int
)
data class Message2(
val name: String,
val nickName: String
)
And the respective endpoint:
#RestController
class MessageController {
#GetMapping("/messages")
fun getEndpoints(): List<Message> {
return listOf(
Message1("Marco", 22),
Message2("Polo", "Poli")
)
}
}
So far so good - but now I want another endpoint that uses one of the explicit classes and I get a serialization error in my test that #class is missing - I don't want to send that when I'm using a concrete class anyhow.
#RestController
class MessageController {
#PostMapping("/add1")
fun add(#RequestBody content: Message1) {
// do something
}
}
org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Missing type id when trying to resolve subtype of [simple type, class com.tractive.tap.message.request.RequestDataDTO]: missing type id property '#class'; nested exception is com.fasterxml.jackson.databind.exc.InvalidTypeIdException: Missing type id when trying to resolve subtype of [simple type, class com.tractive.tap.message.request.RequestDataDTO]: missing type id property '#class'
at [Source: (PushbackInputStream); line: 1, column: 51]
Why is the #class expected even though I'm using a concrete class? Is this expected behavior or am I doing something wrong?
Well, it is expected because by annotating the class with #JsonTypeInfo - via class-interface inheritance - you have explicitly instructed your Jackson to expect this information.
#JsonTypeInfo accepts parameter defaultImpl of type Class<?> that will be used
if type identifier is either not present, or can not be mapped to a registered type
You could use that to default your deserialisation to one type of message, preferably the most widely used explicitly in your api. For other types, you would still have to include the class info for Jackson.

Spring boot validation annotation not working for Kotlin

I got a simple data class for Kotlin
data class Person(val name: String, #get: Min(18) val age: Int)
I actually build this class from a CSV file and I read the CSV using apache CSV parser. Even I have some data which is less than 18 for age field, the test still passed no error.
Looks like this annotation is not working for Kotlin?
You're currently annotating the constructor parameter with the Min annotation - I believe you should annotate the field instead, with a use-site target like this:
data class Person(val name: String, #field:Min(18) val age: Int)
Try adding the annotation#Validated to the class (might just work for spring beans though). These annotations do not prevent the value being set they simply allow getting a BindingResult with a list of validation errors from an existing instance. If you need to prevent a value being set you can use a custom delegate.
val age: Int by MyDelegates.min(18)

Resources