micronaut-projects / micronaut-core

Micronaut Application Framework
http://micronaut.io
Apache License 2.0
6.09k stars 1.07k forks source link

ConversionErrorHandler not being used after upgrading to 4.4.2 #10808

Open GeitV opened 6 months ago

GeitV commented 6 months ago

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

  1. 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(".")
        }
    }
    }
  2. Send POST request to endpoint, that has non-null field OR send POST request to endpoint with unknown enum

Environment Information

Example Application

No response

Version

4.4.2

davidsonnabend commented 4 months ago

Any updates on this one? This bug prevents us from updating to the latest Micronaut version.

dstepanov commented 1 month ago

Please create a sample app that is reproducing the problem

davidsonnabend commented 1 month ago

I was not able to create a reproducer using Micronaut 4.6.3. Either the problem no longer exists or we had a different issue.