ribesg
06/14/2023, 12:28 PMribesg
06/14/2023, 12:47 PMsuspend inline fun <reified T : Any> T.validated(noinline block: suspend T.() -> ValidationResult): T {
val result = block()
if (result is ValidationResult.Invalid) {
throw RequestValidationException(this, result.reasons)
}
return this
}
post<AuthResource> { resource ->
val payload = call.receive<AuthResource.Payload>().validated {
val reasons = mutableListOf<String>()
if (username.isBlank()) {
reasons += "username should not be blank"
}
if (password.isBlank()) {
reasons += "password should not be blank"
}
if (appId != null && !AppId.isValid(appId)) {
reasons += "appId is invalid"
}
if (reasons.isEmpty()) {
ValidationResult.Valid
} else {
ValidationResult.Invalid(reasons)
}
}
…
}
ribesg
06/14/2023, 12:56 PMclass ValidationScope {
private val _invalidationReasons = mutableListOf<String>()
val invalidationReasons: List<String> get() = _invalidationReasons
fun invalidate(reason: String) {
_invalidationReasons += reason
}
}
fun <T : Any> T.validated(
block: context(ValidationScope) T.() -> Unit,
): T {
val scope = ValidationScope()
block(scope, this)
if (scope.invalidationReasons.isNotEmpty()) {
throw RequestValidationException(this, scope.invalidationReasons)
}
return this
}
post<AuthResource> { resource ->
val payload = call.receive<AuthResource.Payload>().validated {
if (username.isBlank()) {
invalidate("username should not be blank")
}
if (password.isBlank()) {
invalidate("password should not be blank")
}
if (appId != null && !AppId.isValid(appId)) {
invalidate("appId is invalid")
}
}
…
}
Nigel Smith
06/14/2023, 1:51 PMresource<MessageResource> {
install(ContentNegotiation) {
jackson(block = jacksonOptions)
}
}
Nigel Smith
06/14/2023, 1:51 PMresource<T>
ribesg
06/14/2023, 2:44 PMresource<T>(…)
, I was lookin for route<T>(…)
. I'm still trying to figure out why I would need the plugin, and how to also handle serialization issues, which the plugin does not handleribesg
06/14/2023, 2:46 PMusername
is blank I have an exception somewhere, but if the field is missing from the body it's a different error somewhere else, which is harder to catch.
In the past I used to have a Weak
data class mimicking my payload class but with all fields nullable with default value null
, and then have a function converting it to the actual payload, but I don't like itAleksei Tirman [JB]
06/14/2023, 3:50 PMRequestValidation
plugin or that each exception contains only a message without specific information about the missing fields?ribesg
06/14/2023, 4:10 PMribesg
06/14/2023, 4:11 PM