Home > OS >  Webclient raises exceptions on responses with non-200 status codes. How can I return the response an
Webclient raises exceptions on responses with non-200 status codes. How can I return the response an

Time:11-18

When using Spring Webclient, any non-200 status code immediately throws an exception. I do not want this behavior.

I have the following API Service:

fun createSomething(body: String): SomeCustomResponse {

        // Get signedRequest from another service (see below for that code)

        return signedRequest
            .retrieve()
            .bodyToMono(SomeCustomResponse::class.java)
            .block()
}

My response class intentionally contains an error field:


data class SomeCustomResponse(
    val message: String? = null,
    val error: String? = null
)

I want to pass the errors returned from this request to the front end, so the user can see the error messages. As long as the status code is 200, that works exactly as it should, but any non-200 status code throws immediately.

I've tried using .onStatus, but you can only return Throwable Monos using that, which I do not want to do. That attempted solution looks like this:

        return signedRequest
            .retrieve()
            .onStatus(HttpStatus::is4xxClientError) {
                it.bodyToMono(SomeCustomResponse::class.java)
            }
            .bodyToMono(SomeCustomResponse::class.java)
            .block()

But that doesn't compile, complaining about:

Type mismatch: inferred type is Mono<SomeCustomResponse!>! but Mono<out Throwable!>! was expected Type mismatch: inferred type is SomeCustomResponse! but Throwable! was expected

The signedRequest value is of type RequestHeadersSpec<*>, which is an AWS v4 signed API request. I cannot put custom logic into that service, as it is used for any number of other APIs.

For reference though, that code looks like this:

// Generate Auth headers and then:

        return WebClient.create(baseUrl)
            .method(method)
            .uri { builder: UriBuilder -> builder.path(resourcePath).build() }
            .header("X-Amz-Date", headers["X-Amz-Date"])
            .header("Authorization", headers["Authorization"])
            .body(Mono.just(body), String::class.java)

How can I bypass the default behavior of throwing exceptions here? I just want to return the response exactly as it currently does, regardless of status code.

CodePudding user response:

According to the ResponseSpec.onStatus docs:

To suppress the treatment of a status code as an error and process it as a normal response, return Mono.empty() from the function. The response will then propagate downstream to be processed.

return signedRequest
    .retrieve()
    .onStatus(HttpStatus::is4xxClientError) {
        Mono.empty()
    }
    .bodyToMono(SomeCustomResponse::class.java)
    .block()

Bear in mind though, that if the error is a "real one" (e.g. the URL indeed does not exist) and the response body is not parseable, the method will throw a pretty misleading deserialisation exception.

  • Related