diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/DefaultWebClient.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/DefaultWebClient.java index d2ad4f1f90..1a339b4f26 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/DefaultWebClient.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/DefaultWebClient.java @@ -27,9 +27,11 @@ import java.util.Arrays; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.function.BiFunction; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; +import java.util.function.Supplier; import org.reactivestreams.Publisher; import reactor.core.publisher.Flux; @@ -39,6 +41,7 @@ import org.springframework.core.ParameterizedTypeReference; import org.springframework.core.io.buffer.DataBufferUtils; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; +import org.springframework.http.HttpRequest; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.client.reactive.ClientHttpRequest; @@ -139,7 +142,6 @@ class DefaultWebClient implements WebClient { return methodInternal(httpMethod); } - @SuppressWarnings("unchecked") private RequestBodyUriSpec methodInternal(HttpMethod httpMethod) { return new DefaultRequestBodyUriSpec(httpMethod); } @@ -364,7 +366,35 @@ class DefaultWebClient implements WebClient { @Override public ResponseSpec retrieve() { - return new DefaultResponseSpec(exchange()); + return new DefaultResponseSpec(exchange(), this::createRequest); + } + + private HttpRequest createRequest() { + return new HttpRequest() { + + private HttpHeaders headers = initHeaders(); + + + @Override + public HttpMethod getMethod() { + return httpMethod; + } + + @Override + public String getMethodValue() { + return httpMethod.name(); + } + + @Override + public URI getURI() { + return uri; + } + + @Override + public HttpHeaders getHeaders() { + return headers; + } + }; } } @@ -376,11 +406,15 @@ class DefaultWebClient implements WebClient { private final Mono responseMono; + private final Supplier requestSupplier; + private final List statusHandlers = new ArrayList<>(1); - DefaultResponseSpec(Mono responseMono) { + DefaultResponseSpec(Mono responseMono, + Supplier requestSupplier) { this.responseMono = responseMono; + this.requestSupplier = requestSupplier; this.statusHandlers.add(DEFAULT_STATUS_HANDLER); } @@ -393,7 +427,8 @@ class DefaultWebClient implements WebClient { if (this.statusHandlers.size() == 1 && this.statusHandlers.get(0) == DEFAULT_STATUS_HANDLER) { this.statusHandlers.clear(); } - this.statusHandlers.add(new StatusHandler(statusPredicate, exceptionFunction)); + this.statusHandlers.add(new StatusHandler(statusPredicate, + (clientResponse, request) -> exceptionFunction.apply(clientResponse))); return this; } @@ -405,21 +440,18 @@ class DefaultWebClient implements WebClient { } @Override - @SuppressWarnings("unchecked") public Mono bodyToMono(ParameterizedTypeReference bodyType) { return this.responseMono.flatMap(response -> handleBody(response, response.bodyToMono(bodyType), mono -> mono.flatMap(Mono::error))); } @Override - @SuppressWarnings("unchecked") public Flux bodyToFlux(Class elementType) { return this.responseMono.flatMapMany(response -> handleBody(response, response.bodyToFlux(elementType), mono -> mono.flatMapMany(Flux::error))); } @Override - @SuppressWarnings("unchecked") public Flux bodyToFlux(ParameterizedTypeReference elementType) { return this.responseMono.flatMapMany(response -> handleBody(response, response.bodyToFlux(elementType), mono -> mono.flatMapMany(Flux::error))); @@ -431,7 +463,8 @@ class DefaultWebClient implements WebClient { if (HttpStatus.resolve(response.rawStatusCode()) != null) { for (StatusHandler handler : this.statusHandlers) { if (handler.test(response.statusCode())) { - Mono exMono = handler.apply(response); + HttpRequest request = this.requestSupplier.get(); + Mono exMono = handler.apply(response, request); exMono = exMono.flatMap(ex -> drainBody(response, ex)); exMono = exMono.onErrorResume(ex -> drainBody(response, ex)); return errorFunction.apply(exMono); @@ -440,7 +473,8 @@ class DefaultWebClient implements WebClient { return bodyPublisher; } else { - return errorFunction.apply(createResponseException(response)); + return errorFunction.apply(createResponseException(response, + this.requestSupplier.get())); } } @@ -452,7 +486,8 @@ class DefaultWebClient implements WebClient { .onErrorResume(ex2 -> Mono.empty()).thenReturn(ex); } - private static Mono createResponseException(ClientResponse response) { + private static Mono createResponseException(ClientResponse response, + HttpRequest request) { return DataBufferUtils.join(response.body(BodyExtractors.toDataBuffers())) .map(dataBuffer -> { byte[] bytes = new byte[dataBuffer.readableByteCount()]; @@ -471,14 +506,16 @@ class DefaultWebClient implements WebClient { response.statusCode().getReasonPhrase(), response.headers().asHttpHeaders(), bodyBytes, - charset); + charset, + request); } else { return new UnknownHttpStatusCodeException( response.rawStatusCode(), response.headers().asHttpHeaders(), bodyBytes, - charset); + charset, + request); } }); } @@ -488,11 +525,11 @@ class DefaultWebClient implements WebClient { private final Predicate predicate; - private final Function> exceptionFunction; + private final BiFunction> exceptionFunction; public StatusHandler(Predicate predicate, - Function> exceptionFunction) { + BiFunction> exceptionFunction) { Assert.notNull(predicate, "Predicate must not be null"); Assert.notNull(exceptionFunction, "Function must not be null"); @@ -505,8 +542,8 @@ class DefaultWebClient implements WebClient { return this.predicate.test(status); } - public Mono apply(ClientResponse response) { - return this.exceptionFunction.apply(response); + public Mono apply(ClientResponse response, HttpRequest request) { + return this.exceptionFunction.apply(response, request); } } } diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/UnknownHttpStatusCodeException.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/UnknownHttpStatusCodeException.java index cf8e65807e..87a3627814 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/UnknownHttpStatusCodeException.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/UnknownHttpStatusCodeException.java @@ -19,6 +19,8 @@ package org.springframework.web.reactive.function.client; import java.nio.charset.Charset; import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpRequest; +import org.springframework.lang.Nullable; /** * Exception thrown when an unknown (or custom) HTTP status code is received. @@ -31,6 +33,10 @@ public class UnknownHttpStatusCodeException extends WebClientResponseException { private static final long serialVersionUID = 2407169540168185007L; + /** + * Create a new instance of the {@code UnknownHttpStatusCodeException} with the given + * parameters. + */ public UnknownHttpStatusCodeException( int statusCode, HttpHeaders headers, byte[] responseBody, Charset responseCharset) { @@ -38,4 +44,17 @@ public class UnknownHttpStatusCodeException extends WebClientResponseException { headers, responseBody, responseCharset); } + /** + * Create a new instance of the {@code UnknownHttpStatusCodeException} with the given + * parameters. + * @since 5.1.4 + */ + public UnknownHttpStatusCodeException( + int statusCode, HttpHeaders headers, byte[] responseBody, Charset responseCharset, + @Nullable HttpRequest request) { + + super("Unknown status code [" + statusCode + "]", statusCode, "", + headers, responseBody, responseCharset, request); + } + } diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/WebClientResponseException.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/WebClientResponseException.java index 86763eb55f..a2feee5568 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/WebClientResponseException.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/WebClientResponseException.java @@ -20,6 +20,7 @@ import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpRequest; import org.springframework.http.HttpStatus; import org.springframework.lang.Nullable; @@ -44,6 +45,9 @@ public class WebClientResponseException extends WebClientException { private final Charset responseCharset; + @Nullable + private final HttpRequest request; + /** * Constructor with response data only, and a default message. @@ -52,22 +56,44 @@ public class WebClientResponseException extends WebClientException { public WebClientResponseException(int statusCode, String statusText, @Nullable HttpHeaders headers, @Nullable byte[] body, @Nullable Charset charset) { - this(statusCode + " " + statusText, statusCode, statusText, headers, body, charset); + this(statusCode, statusText, headers, body, charset, null); + } + + /** + * Constructor with response data only, and a default message. + * @since 5.1.4 + */ + public WebClientResponseException(int statusCode, String statusText, + @Nullable HttpHeaders headers, @Nullable byte[] body, @Nullable Charset charset, + @Nullable HttpRequest request) { + + this(statusCode + " " + statusText, statusCode, statusText, headers, body, charset, request); } /** * Constructor with a prepared message. */ public WebClientResponseException(String message, int statusCode, String statusText, - @Nullable HttpHeaders headers, @Nullable byte[] responsebody, @Nullable Charset charset) { + @Nullable HttpHeaders headers, @Nullable byte[] responseBody, @Nullable Charset charset) { + this(message, statusCode, statusText, headers, responseBody, charset, null); + } + + /** + * Constructor with a prepared message. + * @since 5.1.4 + */ + public WebClientResponseException(String message, int statusCode, String statusText, + @Nullable HttpHeaders headers, @Nullable byte[] responseBody, @Nullable Charset charset, + @Nullable HttpRequest request) { super(message); this.statusCode = statusCode; this.statusText = statusText; this.headers = (headers != null ? headers : HttpHeaders.EMPTY); - this.responseBody = (responsebody != null ? responsebody : new byte[0]); + this.responseBody = (responseBody != null ? responseBody : new byte[0]); this.responseCharset = (charset != null ? charset : StandardCharsets.ISO_8859_1); + this.request = request; } @@ -114,6 +140,14 @@ public class WebClientResponseException extends WebClientException { return new String(this.responseBody, this.responseCharset); } + /** + * Return the corresponding request. + * @since 5.1.4 + */ + @Nullable + public HttpRequest getRequest() { + return this.request; + } /** * Create {@code WebClientResponseException} or an HTTP status specific sub-class. @@ -122,44 +156,55 @@ public class WebClientResponseException extends WebClientException { public static WebClientResponseException create( int statusCode, String statusText, HttpHeaders headers, byte[] body, @Nullable Charset charset) { + return create(statusCode, statusText, headers, body, charset, null); + } + + /** + * Create {@code WebClientResponseException} or an HTTP status specific sub-class. + * @since 5.1.4 + */ + public static WebClientResponseException create( + int statusCode, String statusText, HttpHeaders headers, byte[] body, + @Nullable Charset charset, @Nullable HttpRequest request) { + HttpStatus httpStatus = HttpStatus.resolve(statusCode); if (httpStatus != null) { switch (httpStatus) { case BAD_REQUEST: - return new WebClientResponseException.BadRequest(statusText, headers, body, charset); + return new WebClientResponseException.BadRequest(statusText, headers, body, charset, request); case UNAUTHORIZED: - return new WebClientResponseException.Unauthorized(statusText, headers, body, charset); + return new WebClientResponseException.Unauthorized(statusText, headers, body, charset, request); case FORBIDDEN: - return new WebClientResponseException.Forbidden(statusText, headers, body, charset); + return new WebClientResponseException.Forbidden(statusText, headers, body, charset, request); case NOT_FOUND: - return new WebClientResponseException.NotFound(statusText, headers, body, charset); + return new WebClientResponseException.NotFound(statusText, headers, body, charset, request); case METHOD_NOT_ALLOWED: - return new WebClientResponseException.MethodNotAllowed(statusText, headers, body, charset); + return new WebClientResponseException.MethodNotAllowed(statusText, headers, body, charset, request); case NOT_ACCEPTABLE: - return new WebClientResponseException.NotAcceptable(statusText, headers, body, charset); + return new WebClientResponseException.NotAcceptable(statusText, headers, body, charset, request); case CONFLICT: - return new WebClientResponseException.Conflict(statusText, headers, body, charset); + return new WebClientResponseException.Conflict(statusText, headers, body, charset, request); case GONE: - return new WebClientResponseException.Gone(statusText, headers, body, charset); + return new WebClientResponseException.Gone(statusText, headers, body, charset, request); case UNSUPPORTED_MEDIA_TYPE: - return new WebClientResponseException.UnsupportedMediaType(statusText, headers, body, charset); + return new WebClientResponseException.UnsupportedMediaType(statusText, headers, body, charset, request); case TOO_MANY_REQUESTS: - return new WebClientResponseException.TooManyRequests(statusText, headers, body, charset); + return new WebClientResponseException.TooManyRequests(statusText, headers, body, charset, request); case UNPROCESSABLE_ENTITY: - return new WebClientResponseException.UnprocessableEntity(statusText, headers, body, charset); + return new WebClientResponseException.UnprocessableEntity(statusText, headers, body, charset, request); case INTERNAL_SERVER_ERROR: - return new WebClientResponseException.InternalServerError(statusText, headers, body, charset); + return new WebClientResponseException.InternalServerError(statusText, headers, body, charset, request); case NOT_IMPLEMENTED: - return new WebClientResponseException.NotImplemented(statusText, headers, body, charset); + return new WebClientResponseException.NotImplemented(statusText, headers, body, charset, request); case BAD_GATEWAY: - return new WebClientResponseException.BadGateway(statusText, headers, body, charset); + return new WebClientResponseException.BadGateway(statusText, headers, body, charset, request); case SERVICE_UNAVAILABLE: - return new WebClientResponseException.ServiceUnavailable(statusText, headers, body, charset); + return new WebClientResponseException.ServiceUnavailable(statusText, headers, body, charset, request); case GATEWAY_TIMEOUT: - return new WebClientResponseException.GatewayTimeout(statusText, headers, body, charset); + return new WebClientResponseException.GatewayTimeout(statusText, headers, body, charset, request); } } - return new WebClientResponseException(statusCode, statusText, headers, body, charset); + return new WebClientResponseException(statusCode, statusText, headers, body, charset, request); } @@ -173,9 +218,11 @@ public class WebClientResponseException extends WebClientException { @SuppressWarnings("serial") public static class BadRequest extends WebClientResponseException { - BadRequest(String statusText, HttpHeaders headers, byte[] body, @Nullable Charset charset) { - super(HttpStatus.BAD_REQUEST.value(), statusText, headers, body, charset); + BadRequest(String statusText, HttpHeaders headers, byte[] body, @Nullable Charset charset, + @Nullable HttpRequest request) { + super(HttpStatus.BAD_REQUEST.value(), statusText, headers, body, charset, request); } + } /** @@ -185,8 +232,9 @@ public class WebClientResponseException extends WebClientException { @SuppressWarnings("serial") public static class Unauthorized extends WebClientResponseException { - Unauthorized(String statusText, HttpHeaders headers, byte[] body, @Nullable Charset charset) { - super(HttpStatus.UNAUTHORIZED.value(), statusText, headers, body, charset); + Unauthorized(String statusText, HttpHeaders headers, byte[] body, @Nullable Charset charset, + @Nullable HttpRequest request) { + super(HttpStatus.UNAUTHORIZED.value(), statusText, headers, body, charset, request); } } @@ -197,8 +245,9 @@ public class WebClientResponseException extends WebClientException { @SuppressWarnings("serial") public static class Forbidden extends WebClientResponseException { - Forbidden(String statusText, HttpHeaders headers, byte[] body, @Nullable Charset charset) { - super(HttpStatus.FORBIDDEN.value(), statusText, headers, body, charset); + Forbidden(String statusText, HttpHeaders headers, byte[] body, @Nullable Charset charset, + @Nullable HttpRequest request) { + super(HttpStatus.FORBIDDEN.value(), statusText, headers, body, charset, request); } } @@ -209,8 +258,9 @@ public class WebClientResponseException extends WebClientException { @SuppressWarnings("serial") public static class NotFound extends WebClientResponseException { - NotFound(String statusText, HttpHeaders headers, byte[] body, @Nullable Charset charset) { - super(HttpStatus.NOT_FOUND.value(), statusText, headers, body, charset); + NotFound(String statusText, HttpHeaders headers, byte[] body, @Nullable Charset charset, + @Nullable HttpRequest request) { + super(HttpStatus.NOT_FOUND.value(), statusText, headers, body, charset, request); } } @@ -221,8 +271,10 @@ public class WebClientResponseException extends WebClientException { @SuppressWarnings("serial") public static class MethodNotAllowed extends WebClientResponseException { - MethodNotAllowed(String statusText, HttpHeaders headers, byte[] body, @Nullable Charset charset) { - super(HttpStatus.METHOD_NOT_ALLOWED.value(), statusText, headers, body, charset); + MethodNotAllowed(String statusText, HttpHeaders headers, byte[] body, + @Nullable Charset charset, @Nullable HttpRequest request) { + super(HttpStatus.METHOD_NOT_ALLOWED.value(), statusText, headers, body, charset, + request); } } @@ -233,8 +285,9 @@ public class WebClientResponseException extends WebClientException { @SuppressWarnings("serial") public static class NotAcceptable extends WebClientResponseException { - NotAcceptable(String statusText, HttpHeaders headers, byte[] body, @Nullable Charset charset) { - super(HttpStatus.NOT_ACCEPTABLE.value(), statusText, headers, body, charset); + NotAcceptable(String statusText, HttpHeaders headers, byte[] body, + @Nullable Charset charset, @Nullable HttpRequest request) { + super(HttpStatus.NOT_ACCEPTABLE.value(), statusText, headers, body, charset, request); } } @@ -245,8 +298,9 @@ public class WebClientResponseException extends WebClientException { @SuppressWarnings("serial") public static class Conflict extends WebClientResponseException { - Conflict(String statusText, HttpHeaders headers, byte[] body, @Nullable Charset charset) { - super(HttpStatus.CONFLICT.value(), statusText, headers, body, charset); + Conflict(String statusText, HttpHeaders headers, byte[] body, @Nullable Charset charset, + @Nullable HttpRequest request) { + super(HttpStatus.CONFLICT.value(), statusText, headers, body, charset, request); } } @@ -257,8 +311,9 @@ public class WebClientResponseException extends WebClientException { @SuppressWarnings("serial") public static class Gone extends WebClientResponseException { - Gone(String statusText, HttpHeaders headers, byte[] body, @Nullable Charset charset) { - super(HttpStatus.GONE.value(), statusText, headers, body, charset); + Gone(String statusText, HttpHeaders headers, byte[] body, @Nullable Charset charset, + @Nullable HttpRequest request) { + super(HttpStatus.GONE.value(), statusText, headers, body, charset, request); } } @@ -269,8 +324,11 @@ public class WebClientResponseException extends WebClientException { @SuppressWarnings("serial") public static class UnsupportedMediaType extends WebClientResponseException { - UnsupportedMediaType(String statusText, HttpHeaders headers, byte[] body, @Nullable Charset charset) { - super(HttpStatus.UNSUPPORTED_MEDIA_TYPE.value(), statusText, headers, body, charset); + UnsupportedMediaType(String statusText, HttpHeaders headers, byte[] body, + @Nullable Charset charset, @Nullable HttpRequest request) { + + super(HttpStatus.UNSUPPORTED_MEDIA_TYPE.value(), statusText, headers, body, charset, + request); } } @@ -281,8 +339,10 @@ public class WebClientResponseException extends WebClientException { @SuppressWarnings("serial") public static class UnprocessableEntity extends WebClientResponseException { - UnprocessableEntity(String statusText, HttpHeaders headers, byte[] body, @Nullable Charset charset) { - super(HttpStatus.UNPROCESSABLE_ENTITY.value(), statusText, headers, body, charset); + UnprocessableEntity(String statusText, HttpHeaders headers, byte[] body, + @Nullable Charset charset, @Nullable HttpRequest request) { + super(HttpStatus.UNPROCESSABLE_ENTITY.value(), statusText, headers, body, charset, + request); } } @@ -293,8 +353,10 @@ public class WebClientResponseException extends WebClientException { @SuppressWarnings("serial") public static class TooManyRequests extends WebClientResponseException { - TooManyRequests(String statusText, HttpHeaders headers, byte[] body, @Nullable Charset charset) { - super(HttpStatus.TOO_MANY_REQUESTS.value(), statusText, headers, body, charset); + TooManyRequests(String statusText, HttpHeaders headers, byte[] body, + @Nullable Charset charset, @Nullable HttpRequest request) { + super(HttpStatus.TOO_MANY_REQUESTS.value(), statusText, headers, body, charset, + request); } } @@ -309,8 +371,10 @@ public class WebClientResponseException extends WebClientException { @SuppressWarnings("serial") public static class InternalServerError extends WebClientResponseException { - InternalServerError(String statusText, HttpHeaders headers, byte[] body, @Nullable Charset charset) { - super(HttpStatus.INTERNAL_SERVER_ERROR.value(), statusText, headers, body, charset); + InternalServerError(String statusText, HttpHeaders headers, byte[] body, + @Nullable Charset charset, @Nullable HttpRequest request) { + super(HttpStatus.INTERNAL_SERVER_ERROR.value(), statusText, headers, body, charset, + request); } } @@ -321,8 +385,9 @@ public class WebClientResponseException extends WebClientException { @SuppressWarnings("serial") public static class NotImplemented extends WebClientResponseException { - NotImplemented(String statusText, HttpHeaders headers, byte[] body, @Nullable Charset charset) { - super(HttpStatus.NOT_IMPLEMENTED.value(), statusText, headers, body, charset); + NotImplemented(String statusText, HttpHeaders headers, byte[] body, + @Nullable Charset charset, @Nullable HttpRequest request) { + super(HttpStatus.NOT_IMPLEMENTED.value(), statusText, headers, body, charset, request); } } @@ -333,8 +398,9 @@ public class WebClientResponseException extends WebClientException { @SuppressWarnings("serial") public static class BadGateway extends WebClientResponseException { - BadGateway(String statusText, HttpHeaders headers, byte[] body, @Nullable Charset charset) { - super(HttpStatus.BAD_GATEWAY.value(), statusText, headers, body, charset); + BadGateway(String statusText, HttpHeaders headers, byte[] body, @Nullable Charset charset, + @Nullable HttpRequest request) { + super(HttpStatus.BAD_GATEWAY.value(), statusText, headers, body, charset, request); } } @@ -345,8 +411,10 @@ public class WebClientResponseException extends WebClientException { @SuppressWarnings("serial") public static class ServiceUnavailable extends WebClientResponseException { - ServiceUnavailable(String statusText, HttpHeaders headers, byte[] body, @Nullable Charset charset) { - super(HttpStatus.SERVICE_UNAVAILABLE.value(), statusText, headers, body, charset); + ServiceUnavailable(String statusText, HttpHeaders headers, byte[] body, + @Nullable Charset charset, @Nullable HttpRequest request) { + super(HttpStatus.SERVICE_UNAVAILABLE.value(), statusText, headers, body, charset, + request); } } @@ -357,8 +425,10 @@ public class WebClientResponseException extends WebClientException { @SuppressWarnings("serial") public static class GatewayTimeout extends WebClientResponseException { - GatewayTimeout(String statusText, HttpHeaders headers, byte[] body, @Nullable Charset charset) { - super(HttpStatus.GATEWAY_TIMEOUT.value(), statusText, headers, body, charset); + GatewayTimeout(String statusText, HttpHeaders headers, byte[] body, + @Nullable Charset charset, @Nullable HttpRequest request) { + super(HttpStatus.GATEWAY_TIMEOUT.value(), statusText, headers, body, charset, + request); } } diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/function/client/WebClientIntegrationTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/function/client/WebClientIntegrationTests.java index 72c08b13c0..c75dc20774 100644 --- a/spring-webflux/src/test/java/org/springframework/web/reactive/function/client/WebClientIntegrationTests.java +++ b/spring-webflux/src/test/java/org/springframework/web/reactive/function/client/WebClientIntegrationTests.java @@ -18,6 +18,7 @@ package org.springframework.web.reactive.function.client; import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.net.URI; import java.nio.file.Files; import java.time.Duration; import java.util.Arrays; @@ -45,6 +46,8 @@ import org.springframework.core.io.buffer.DataBuffer; import org.springframework.core.io.buffer.DataBufferUtils; import org.springframework.core.io.buffer.DefaultDataBufferFactory; import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpRequest; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; @@ -494,8 +497,9 @@ public class WebClientIntegrationTests { prepareResponse(response -> response.setResponseCode(500) .setHeader("Content-Type", "text/plain").setBody(errorMessage)); + String path = "/greeting?name=Spring"; Mono result = this.webClient.get() - .uri("/greeting?name=Spring") + .uri(path) .retrieve() .bodyToMono(String.class); @@ -509,13 +513,18 @@ public class WebClientIntegrationTests { ex.getStatusText()); assertEquals(MediaType.TEXT_PLAIN, ex.getHeaders().getContentType()); assertEquals(errorMessage, ex.getResponseBodyAsString()); + + HttpRequest request = ex.getRequest(); + assertEquals(HttpMethod.GET, request.getMethod()); + assertEquals(URI.create(this.server.url(path).toString()), request.getURI()); + assertNotNull(request.getHeaders()); }) .verify(Duration.ofSeconds(3)); expectRequestCount(1); expectRequest(request -> { assertEquals("*/*", request.getHeader(HttpHeaders.ACCEPT)); - assertEquals("/greeting?name=Spring", request.getPath()); + assertEquals(path, request.getPath()); }); }