micronaut-core
micronaut-core copied to clipboard
ConversionErrorHandler not being used after upgrading to 4.4.2
Expected Behavior
We got custom ExceptionHandler
that replaced ConversionErrorHandler
which worked just fine on 4.3.5. The codebase is Kotlin and this catched errors where data model field was not nullable, but given input was null (so the error response would match what the @Validated gives)
Actual Behaviour
After upgrade to 4.4.2, we are getting INTERNAL_SERVER_ERROR
instead. Seems like when before the ConversionErrorHandler was used, then it's not any more on nullpointer exceptions.
{
"direction": "Outgoing",
"response": "500 Internal Server Error",
"duration": "271 ms",
"body": {
"type": "about:blank",
"status": 500,
"detail": "Internal Server Error: Parameter specified as non-null is null: method xx.xx.xxx.model.ModelDto.<init>, parameter firstName",
"parameters": {}
}
}
Steps To Reproduce
- Add custom request ExceptionHandler:
@Singleton
@Produces
@Replaces(ConversionErrorHandler::class)
class JsonExceptionHandler(
private val problemErrorResponseProcessor: ProblemErrorResponseProcessor
) : ExceptionHandler<ConversionErrorException, HttpResponse<*>> {
override fun handle(request: HttpRequest<*>, exception: ConversionErrorException): HttpResponse<*> {
val exceptionMessage = exception.toString()
if (exceptionMessage.contains("No enum constant")) {
return processEnumError(request, exception.conversionError.cause.toString())
}
if (exceptionMessage.contains("Parameter specified as non-null is null") ||
exceptionMessage.contains(Regex("for value \\[(NaN|undefined|null)] due to"))
) {
return processNullFieldError(request, exception.conversionError.cause.toString())
}
log.error(exception) { "Got exception from parsing request" }
return HttpResponse.serverError(exception)
}
private fun processNullFieldError(request: HttpRequest<*>, cause: String): HttpResponse<*> {
val field = getFieldPath(cause)
val e = ValidationUtil.createValidationError(field, "Must not be null")
val response = HttpResponse.status<Problem>(HttpStatus.BAD_REQUEST)
return problemErrorResponseProcessor.processResponse(
ErrorContext.builder(request).cause(e).errorMessage(e.message).build(),
response
)
}
private fun processEnumError(request: HttpRequest<*>, cause: String): HttpResponse<*> {
val field = getFieldPath(cause)
val value = enumRegex.find(cause)?.value
val e = ValidationUtil.createValidationError(field, "Unknown enum value '$value'", value)
val response = HttpResponse.status<Problem>(HttpStatus.BAD_REQUEST)
return problemErrorResponseProcessor.processResponse(
ErrorContext.builder(request).cause(e).errorMessage(e.message).build(),
response
)
}
companion object {
private val log = KotlinLogging.logger {}
private val fieldRegex = Regex("\\[(?!class)[^]]*\\s([^]]*)]")
private val enumRegex = Regex("[^.]*\$")
fun getFieldPath(errorMessage: String): String {
val matches = fieldRegex.findAll(errorMessage)
return matches.map { it.groupValues[1] }.joinToString(".")
}
}
}
- Send POST request to endpoint, that has non-null field OR send POST request to endpoint with unknown enum
Environment Information
- Kotlin 1.9.23
Example Application
No response
Version
4.4.2