micronaut-core: Error Handling (`@Error`) for HttpClientResponseException does not provide response.body (body is null / empty)

Running on micronaut 1.2.6 and Kotlin 1.3.50.

Consider the following snippet:

@Client("\${formValidatorService.host}")
interface FormValidatorServiceClient {

    @Post("/validate")
    fun validate(@Body validationRequest: Single<FormSubmission>): Single<HttpResponse<ValidationResponseValid>>

}

@Controller("/forms")
open class FormSubmissionController(private val formValidatorServiceClient: FormValidatorServiceClient) : FormSubmissionOperations {

    override fun validate(submission: Single<FormSubmission>): Single<HttpResponse<ValidationResponseValid>> {
        return formValidatorServiceClient.validate(submission).map {
            HttpResponse.ok(it.body()!!)
        }
    }

    @Error
    fun formSubmissionException(request: HttpRequest<*>, exception: HttpClientResponseException): Single<HttpResponse<JsonError>> {
        println(exception.response.body())
        return Single.just(
                HttpResponse.status<JsonError>(HttpStatus.BAD_REQUEST, "Form Validation Failure")
                        .body(JsonError(exception.response.body().toString()))
        )
    }
}


@Validated
interface FormSubmissionOperations {

    @Post(value = "/validate", consumes = [MediaType.APPLICATION_JSON], produces = [MediaType.APPLICATION_JSON])
    fun validate(@Body submission: Single<FormSubmission>): Single<HttpResponse<ValidationResponseValid>>
}

When /validate is run the @Error is triggered by a 400 response returned by the @Client, but the body is always null.

Using TRACE on the HTTP Client, I can confirm that valid JSON is being returned in the response of the HTTP Client’s request.

Also using response.getBody(BodyType), returns Optional.empty

Issue seems very similar to https://github.com/micronaut-projects/micronaut-core/issues/416

About this issue

  • Original URL
  • State: closed
  • Created 5 years ago
  • Comments: 24 (12 by maintainers)

Most upvoted comments

If some poor soul is still trying to figure out what to do in this one, the way it worked for me was:

Stream

handle the exception in the stream and make sure to parse the body (with the error) don’t throw or rethrow the exception that’s is set to happen .doOnError(throwable -> {

                if (throwable instanceof HttpClientResponseException) {
                    HttpClientResponseException ee = (HttpClientResponseException) throwable;

                    //must be called to process the body

                    var error_ = ee.getResponse().getBody(MyErrorDto.class);

                }

Error handler

create an handler for that error

@Error
public HttpResponse<MyResponseResponse> error(HttpClientResponseException ex) {
    var error_ = ex.getResponse().getBody(MyErrorDto.class).orElse(new MyErrorDto());

    return HttpResponse.<MyResponseResponse>status(HttpStatus.BAD_REQUEST).body(error_);
}

If you remove the doOnError() cycle the body value will be dumped, try out by yourself

Hope it helps.

I was able to get response in reactive mode with below example

  .onErrorMap { error ->
                            if (error is HttpClientResponseException) {
                                val ex = error.response.getBody(MyException::class.java)
                                //do handle response
                            }
                          }

That is one way to go about it, however only really necessary for blocking operations. If you are processing the exception in the error handler of a reactive response, the body is available to be converted directly. exception.response.getBody(SomeType). The reason it isn’t available to do so in blocking operations is because the body must be released and we wouldn’t have any idea when that should be done if the client is no longer part of the flow.