Skip to content

WebClient doesn't send error signal for error responses [SPR-15946] #20499

@spring-projects-issues

Description

@spring-projects-issues

Abhijit Sarkar opened SPR-15946 and commented

Actually using 5.0.0.BUILD-SNAPSHOT, forced to choose RC3 as SNAPSHOT isn't available in the list.

With the latest SNAPSHOT, the WebClient.retrieve behavior has changed such that if the response has status code 4xx or 5xx, it returns a successful Mono<? extends Throwable> instead of a Mono with error. This makes no sense and completely breaks the client code. Because the ResponseSpec has no isSuccessful method or something along that line, the client is completely unaware that the Mono now contains a Throwable, and gets a ClassCastException as follows:

java.lang.ClassCastException: org.springframework.web.reactive.function.client.WebClientResponseException cannot be cast to java.lang.String

The expected behavior would be that the ResponseSpec.onStatus method return an error Mono, or the various bodyTo* methods would handle this complication. It is simply not the client's problem.

Here's an alternative implementation that solves this problem; it's in Kotlin but the error handling is lifted straight from DefaultWebClient:

fun ClientResponse.isError(): Boolean = this.statusCode().is4xxClientError || this.statusCode().is5xxServerError

private inline fun <reified T> ClientResponse.toMono(): Mono<T> {
    return if (this.isError()) {
        this.body(BodyExtractors.toDataBuffers())
                .reduce { obj, buffers -> obj.write(buffers) }
                .map { dataBuffer ->
                    dataBuffer.readableByteCount()
                            .let { ByteArray(it) }
                            .also { dataBuffer.read(it) }
                            .apply { DataBufferUtils.release(dataBuffer) }
                }
                .map { bodyBytes ->
                    val msg = "${this.statusCode().value()} ${this.statusCode().reasonPhrase}"
                    val charset = this.headers().contentType()
                            .map { it.charset }
                            .orElse(StandardCharsets.UTF_8)
                    WebClientResponseException(
                            msg,
                            this.statusCode().value(),
                            this.statusCode().reasonPhrase,
                            this.headers().asHttpHeaders(),
                            bodyBytes,
                            charset
                    )
                }
                .flatMap { Mono.error<T>(it) }
    } else {
        this.bodyToMono(T::class.java)
    }
}

Affects: 5.0 RC3

Attachments:

Metadata

Metadata

Assignees

Labels

in: webIssues in web modules (web, webmvc, webflux, websocket)type: bugA general bug

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions