From 038af9a303aa631a66384542eef8c3f0c955d2f8 Mon Sep 17 00:00:00 2001 From: Brian Clozel Date: Thu, 12 Jul 2018 19:08:08 +0200 Subject: [PATCH] Improve unknown status codes handling by WebClient Prior to this commit, `WebClient` would throw `IllegalArgumentException` when receiving an HTTP response with an unknown HTTP status code. This commit is a follow up of SPR-16748 (supporting non-standard HTTP status codes on the reactive `ClientHttpResponse`), and is mirroring SPR-15978 (supporting non-standard HTTP status codes in `RestTemplate`). With this change, `WebClient` now tolerates unknown status codes in some cases, while not offering that choice as a first class citizen: `HttpStatus` is still the preferred way to deal with HTTP status codes. Here's how `WebClient` will behave when fetching the full response: ``` // Given a remote endpoint returning a "123" HTTP status code Mono result = this.webClient.get() .uri("/status/123") .exchange(); // will still throw an IllegalArgumentException HttpStatus status = result.block().statusCode(); // is safe and will return 123 int statusCode = result.block().rawStatusCode(); ``` Resolving directly the response body with `retrieve()` is different. ``` // will send an error signal with a UnknownHttpStatusCodeException Mono result = this.webClient.get() .uri("/status/123") .retrieve() .bodyToMono(String.class); ``` In general, `WebClient` will provide high-level support for well-known HTTP status codes, like error handling with `WebClient.ResponseSpec#onStatus`. For such support with unknown status codes, it is better to rely on lower level constructs such as `ExchangeFilterFunction`. Issue: SPR-16819 --- .../function/client/ClientResponse.java | 12 +++ .../client/DefaultClientResponse.java | 29 +++++-- .../function/client/DefaultWebClient.java | 47 ++++++---- .../UnknownHttpStatusCodeException.java | 23 +++++ .../client/WebClientResponseException.java | 4 +- .../client/support/ClientResponseWrapper.java | 5 ++ .../server/HandlerFilterFunction.java | 4 +- .../client/DefaultClientResponseTests.java | 85 ++++++++++++++++++- .../client/WebClientIntegrationTests.java | 64 +++++++++++++- .../support/ClientResponseWrapperTests.java | 8 ++ 10 files changed, 253 insertions(+), 28 deletions(-) create mode 100644 spring-webflux/src/main/java/org/springframework/web/reactive/function/client/UnknownHttpStatusCodeException.java diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/ClientResponse.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/ClientResponse.java index ae2c0518460..6c322333e99 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/ClientResponse.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/ClientResponse.java @@ -60,9 +60,21 @@ public interface ClientResponse { /** * Return the status code of this response. + * @return the status as an HttpStatus enum value + * @throws IllegalArgumentException in case of an unknown HTTP status code + * @see HttpStatus#valueOf(int) */ HttpStatus statusCode(); + /** + * Return the (potentially non-standard) status code of this response. + * @return the status as an integer + * @since 5.1 + * @see #statusCode() + * @see HttpStatus#resolve(int) + */ + int rawStatusCode(); + /** * Return the headers of this response. */ diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/DefaultClientResponse.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/DefaultClientResponse.java index b2515eacdbe..f39a5d56f37 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/DefaultClientResponse.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/DefaultClientResponse.java @@ -76,6 +76,11 @@ class DefaultClientResponse implements ClientResponse { return this.response.getStatusCode(); } + @Override + public int rawStatusCode() { + return this.response.getRawStatusCode(); + } + @Override public Headers headers() { return this.headers; @@ -177,11 +182,11 @@ class DefaultClientResponse implements ClientResponse { private Mono> toEntityInternal(Mono bodyMono) { HttpHeaders headers = headers().asHttpHeaders(); - HttpStatus statusCode = statusCode(); + int status = rawStatusCode(); return bodyMono - .map(body -> new ResponseEntity<>(body, headers, statusCode)) + .map(body -> createEntity(body, headers, status)) .switchIfEmpty(Mono.defer( - () -> Mono.just(new ResponseEntity<>(headers, statusCode)))); + () -> Mono.just(createEntity(headers, status)))); } @Override @@ -196,10 +201,24 @@ class DefaultClientResponse implements ClientResponse { private Mono>> toEntityListInternal(Flux bodyFlux) { HttpHeaders headers = headers().asHttpHeaders(); - HttpStatus statusCode = statusCode(); + int status = rawStatusCode(); return bodyFlux .collectList() - .map(body -> new ResponseEntity<>(body, headers, statusCode)); + .map(body -> createEntity(body, headers, status)); + } + + private ResponseEntity createEntity(HttpHeaders headers, int status) { + HttpStatus resolvedStatus = HttpStatus.resolve(status); + return resolvedStatus != null + ? new ResponseEntity<>(headers, resolvedStatus) + : ResponseEntity.status(status).headers(headers).build(); + } + + private ResponseEntity createEntity(T body, HttpHeaders headers, int status) { + HttpStatus resolvedStatus = HttpStatus.resolve(status); + return resolvedStatus != null + ? new ResponseEntity<>(body, headers, resolvedStatus) + : ResponseEntity.status(status).headers(headers).body(body); } 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 4606255b251..accadf16512 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 @@ -59,6 +59,7 @@ import org.springframework.web.util.UriBuilderFactory; * Default implementation of {@link WebClient}. * * @author Rossen Stoyanchev + * @author Brian Clozel * @since 5.0 */ class DefaultWebClient implements WebClient { @@ -375,7 +376,7 @@ class DefaultWebClient implements WebClient { private final Mono responseMono; - private List statusHandlers = new ArrayList<>(1); + private final List statusHandlers = new ArrayList<>(1); DefaultResponseSpec(Mono responseMono) { this.responseMono = responseMono; @@ -435,13 +436,17 @@ class DefaultWebClient implements WebClient { private > T bodyToPublisher(ClientResponse response, T bodyPublisher, Function, T> errorFunction) { - - return this.statusHandlers.stream() - .filter(statusHandler -> statusHandler.test(response.statusCode())) - .findFirst() - .map(statusHandler -> statusHandler.apply(response)) - .map(errorFunction::apply) - .orElse(bodyPublisher); + if (HttpStatus.resolve(response.rawStatusCode()) != null) { + return this.statusHandlers.stream() + .filter(statusHandler -> statusHandler.test(response.statusCode())) + .findFirst() + .map(statusHandler -> statusHandler.apply(response)) + .map(errorFunction::apply) + .orElse(bodyPublisher); + } + else { + return errorFunction.apply(createResponseException(response)); + } } private static Mono createResponseException(ClientResponse response) { @@ -454,18 +459,26 @@ class DefaultWebClient implements WebClient { }) .defaultIfEmpty(new byte[0]) .map(bodyBytes -> { - String msg = String.format("ClientResponse has erroneous status code: %d %s", response.statusCode().value(), - response.statusCode().getReasonPhrase()); Charset charset = response.headers().contentType() .map(MimeType::getCharset) .orElse(StandardCharsets.ISO_8859_1); - return new WebClientResponseException(msg, - response.statusCode().value(), - response.statusCode().getReasonPhrase(), - response.headers().asHttpHeaders(), - bodyBytes, - charset - ); + if (HttpStatus.resolve(response.rawStatusCode()) != null) { + String msg = String.format("ClientResponse has erroneous status code: %d %s", + response.statusCode().value(), response.statusCode().getReasonPhrase()); + return new WebClientResponseException(msg, + response.statusCode().value(), + response.statusCode().getReasonPhrase(), + response.headers().asHttpHeaders(), + bodyBytes, + charset); + } + else { + return new UnknownHttpStatusCodeException( + response.rawStatusCode(), + response.headers().asHttpHeaders(), + bodyBytes, + charset); + } }); } 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 new file mode 100644 index 00000000000..90de868edca --- /dev/null +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/UnknownHttpStatusCodeException.java @@ -0,0 +1,23 @@ +package org.springframework.web.reactive.function.client; + +import java.nio.charset.Charset; + +import org.springframework.http.HttpHeaders; + +/** + * Exception thrown when an unknown (or custom) HTTP status code is received. + * + * @author Brian Clozel + * @since 5.1 + */ +public class UnknownHttpStatusCodeException extends WebClientResponseException { + + private static final long serialVersionUID = 2407169540168185007L; + + public UnknownHttpStatusCodeException(int statusCode, HttpHeaders headers, + byte[] responseBody, Charset responseCharset) { + super("Unknown status code [" + statusCode + "]", statusCode, "", + headers, responseBody, responseCharset); + } + +} 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 44d11558d5c..4fdf4ef5871 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 @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -47,6 +47,7 @@ public class WebClientResponseException extends WebClientException { /** * Construct a new instance of with the given response data. + * @param message the exception message * @param statusCode the raw status code value * @param statusText the status text * @param headers the response headers (may be {@code null}) @@ -69,6 +70,7 @@ public class WebClientResponseException extends WebClientException { /** * Return the HTTP status code value. + * @throws IllegalArgumentException in case of an unknown HTTP status code */ public HttpStatus getStatusCode() { return HttpStatus.valueOf(this.statusCode); diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/support/ClientResponseWrapper.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/support/ClientResponseWrapper.java index 3ed3cd1de02..ea236011fd8 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/support/ClientResponseWrapper.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/support/ClientResponseWrapper.java @@ -77,6 +77,11 @@ public class ClientResponseWrapper implements ClientResponse { return this.delegate.statusCode(); } + @Override + public int rawStatusCode() { + return this.delegate.rawStatusCode(); + } + @Override public Headers headers() { return this.delegate.headers(); diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/HandlerFilterFunction.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/HandlerFilterFunction.java index e2c0ed18ca8..a0e8c55733a 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/HandlerFilterFunction.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/HandlerFilterFunction.java @@ -74,7 +74,7 @@ public interface HandlerFilterFunction> messageReaders = Collections @@ -164,6 +174,7 @@ public class DefaultClientResponseTests { httpHeaders.setContentType(MediaType.TEXT_PLAIN); when(mockResponse.getHeaders()).thenReturn(httpHeaders); when(mockResponse.getStatusCode()).thenReturn(HttpStatus.OK); + when(mockResponse.getRawStatusCode()).thenReturn(HttpStatus.OK.value()); when(mockResponse.getBody()).thenReturn(body); List> messageReaders = Collections @@ -187,6 +198,7 @@ public class DefaultClientResponseTests { httpHeaders.setContentType(MediaType.TEXT_PLAIN); when(mockResponse.getHeaders()).thenReturn(httpHeaders); when(mockResponse.getStatusCode()).thenReturn(HttpStatus.OK); + when(mockResponse.getRawStatusCode()).thenReturn(HttpStatus.OK.value()); when(mockResponse.getBody()).thenReturn(body); List> messageReaders = Collections @@ -209,6 +221,7 @@ public class DefaultClientResponseTests { httpHeaders.setContentType(MediaType.TEXT_PLAIN); when(mockResponse.getHeaders()).thenReturn(httpHeaders); when(mockResponse.getStatusCode()).thenReturn(HttpStatus.OK); + when(mockResponse.getRawStatusCode()).thenReturn(HttpStatus.OK.value()); when(mockResponse.getBody()).thenReturn(body); List> messageReaders = Collections @@ -233,6 +246,7 @@ public class DefaultClientResponseTests { httpHeaders.setContentType(MediaType.TEXT_PLAIN); when(mockResponse.getHeaders()).thenReturn(httpHeaders); when(mockResponse.getStatusCode()).thenReturn(HttpStatus.OK); + when(mockResponse.getRawStatusCode()).thenReturn(HttpStatus.OK.value()); when(mockResponse.getBody()).thenReturn(body); List> messageReaders = Collections @@ -242,6 +256,37 @@ public class DefaultClientResponseTests { ResponseEntity result = defaultClientResponse.toEntity(String.class).block(); assertEquals("foo", result.getBody()); assertEquals(HttpStatus.OK, result.getStatusCode()); + assertEquals(HttpStatus.OK.value(), result.getStatusCodeValue()); + assertEquals(MediaType.TEXT_PLAIN, result.getHeaders().getContentType()); + } + + @Test + public void toEntityWithUnknownStatusCode() throws Exception { + DefaultDataBufferFactory factory = new DefaultDataBufferFactory(); + DefaultDataBuffer dataBuffer + = factory.wrap(ByteBuffer.wrap("foo".getBytes(StandardCharsets.UTF_8))); + Flux body = Flux.just(dataBuffer); + + HttpHeaders httpHeaders = new HttpHeaders(); + httpHeaders.setContentType(MediaType.TEXT_PLAIN); + when(mockResponse.getHeaders()).thenReturn(httpHeaders); + when(mockResponse.getStatusCode()).thenThrow(new IllegalArgumentException("999")); + when(mockResponse.getRawStatusCode()).thenReturn(999); + when(mockResponse.getBody()).thenReturn(body); + + List> messageReaders = Collections + .singletonList(new DecoderHttpMessageReader<>(StringDecoder.allMimeTypes())); + when(mockExchangeStrategies.messageReaders()).thenReturn(messageReaders); + + ResponseEntity result = defaultClientResponse.toEntity(String.class).block(); + assertEquals("foo", result.getBody()); + try { + result.getStatusCode(); + fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException ex) { + // do nothing + } + assertEquals(999, result.getStatusCodeValue()); assertEquals(MediaType.TEXT_PLAIN, result.getHeaders().getContentType()); } @@ -256,6 +301,7 @@ public class DefaultClientResponseTests { httpHeaders.setContentType(MediaType.TEXT_PLAIN); when(mockResponse.getHeaders()).thenReturn(httpHeaders); when(mockResponse.getStatusCode()).thenReturn(HttpStatus.OK); + when(mockResponse.getRawStatusCode()).thenReturn(HttpStatus.OK.value()); when(mockResponse.getBody()).thenReturn(body); List> messageReaders = Collections @@ -267,6 +313,7 @@ public class DefaultClientResponseTests { }).block(); assertEquals("foo", result.getBody()); assertEquals(HttpStatus.OK, result.getStatusCode()); + assertEquals(HttpStatus.OK.value(), result.getStatusCodeValue()); assertEquals(MediaType.TEXT_PLAIN, result.getHeaders().getContentType()); } @@ -281,6 +328,7 @@ public class DefaultClientResponseTests { httpHeaders.setContentType(MediaType.TEXT_PLAIN); when(mockResponse.getHeaders()).thenReturn(httpHeaders); when(mockResponse.getStatusCode()).thenReturn(HttpStatus.OK); + when(mockResponse.getRawStatusCode()).thenReturn(HttpStatus.OK.value()); when(mockResponse.getBody()).thenReturn(body); List> messageReaders = Collections @@ -290,6 +338,37 @@ public class DefaultClientResponseTests { ResponseEntity> result = defaultClientResponse.toEntityList(String.class).block(); assertEquals(Collections.singletonList("foo"), result.getBody()); assertEquals(HttpStatus.OK, result.getStatusCode()); + assertEquals(HttpStatus.OK.value(), result.getStatusCodeValue()); + assertEquals(MediaType.TEXT_PLAIN, result.getHeaders().getContentType()); + } + + @Test + public void toEntityListWithUnknownStatusCode() throws Exception { + DefaultDataBufferFactory factory = new DefaultDataBufferFactory(); + DefaultDataBuffer dataBuffer = + factory.wrap(ByteBuffer.wrap("foo".getBytes(StandardCharsets.UTF_8))); + Flux body = Flux.just(dataBuffer); + + HttpHeaders httpHeaders = new HttpHeaders(); + httpHeaders.setContentType(MediaType.TEXT_PLAIN); + when(mockResponse.getHeaders()).thenReturn(httpHeaders); + when(mockResponse.getStatusCode()).thenThrow(new IllegalArgumentException("999")); + when(mockResponse.getRawStatusCode()).thenReturn(999); + when(mockResponse.getBody()).thenReturn(body); + + List> messageReaders = Collections + .singletonList(new DecoderHttpMessageReader<>(StringDecoder.allMimeTypes())); + when(mockExchangeStrategies.messageReaders()).thenReturn(messageReaders); + + ResponseEntity> result = defaultClientResponse.toEntityList(String.class).block(); + assertEquals(Collections.singletonList("foo"), result.getBody()); + try { + result.getStatusCode(); + fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException ex) { + // do nothing + } + assertEquals(999, result.getStatusCodeValue()); assertEquals(MediaType.TEXT_PLAIN, result.getHeaders().getContentType()); } @@ -304,6 +383,7 @@ public class DefaultClientResponseTests { httpHeaders.setContentType(MediaType.TEXT_PLAIN); when(mockResponse.getHeaders()).thenReturn(httpHeaders); when(mockResponse.getStatusCode()).thenReturn(HttpStatus.OK); + when(mockResponse.getRawStatusCode()).thenReturn(HttpStatus.OK.value()); when(mockResponse.getBody()).thenReturn(body); List> messageReaders = Collections @@ -315,6 +395,7 @@ public class DefaultClientResponseTests { }).block(); assertEquals(Collections.singletonList("foo"), result.getBody()); assertEquals(HttpStatus.OK, result.getStatusCode()); + assertEquals(HttpStatus.OK.value(), result.getStatusCodeValue()); assertEquals(MediaType.TEXT_PLAIN, result.getHeaders().getContentType()); } @@ -326,6 +407,7 @@ public class DefaultClientResponseTests { httpHeaders.setContentType(MediaType.TEXT_PLAIN); when(mockResponse.getHeaders()).thenReturn(httpHeaders); when(mockResponse.getStatusCode()).thenReturn(HttpStatus.OK); + when(mockResponse.getRawStatusCode()).thenReturn(HttpStatus.OK.value()); when(mockResponse.getBody()).thenReturn(body.flux()); List> messageReaders = Collections @@ -351,6 +433,7 @@ public class DefaultClientResponseTests { httpHeaders.setContentType(MediaType.TEXT_PLAIN); when(mockResponse.getHeaders()).thenReturn(httpHeaders); when(mockResponse.getStatusCode()).thenReturn(HttpStatus.OK); + when(mockResponse.getRawStatusCode()).thenReturn(HttpStatus.OK.value()); when(mockResponse.getBody()).thenReturn(body.flux()); List> messageReaders = Collections 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 3c4891023c8..c92dce5b125 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 @@ -55,6 +55,7 @@ import static org.junit.Assert.*; * * @author Brian Clozel * @author Rossen Stoyanchev + * @author Denys Ivano */ public class WebClientIntegrationTests { @@ -413,7 +414,7 @@ public class WebClientIntegrationTests { .bodyToMono(String.class); StepVerifier.create(result) - .expectError(WebClientException.class) + .expectError(WebClientResponseException.class) .verify(Duration.ofSeconds(3)); expectRequestCount(1); @@ -433,7 +434,7 @@ public class WebClientIntegrationTests { .bodyToMono(String.class); StepVerifier.create(result) - .expectError(WebClientException.class) + .expectError(WebClientResponseException.class) .verify(Duration.ofSeconds(3)); expectRequestCount(1); @@ -459,6 +460,9 @@ public class WebClientIntegrationTests { assertTrue(throwable instanceof WebClientResponseException); WebClientResponseException ex = (WebClientResponseException) throwable; assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, ex.getStatusCode()); + assertEquals(HttpStatus.INTERNAL_SERVER_ERROR.value(), ex.getRawStatusCode()); + assertEquals(HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase(), + ex.getStatusText()); assertEquals(MediaType.TEXT_PLAIN, ex.getHeaders().getContentType()); assertEquals(errorMessage, ex.getResponseBodyAsString()); }) @@ -471,6 +475,62 @@ public class WebClientIntegrationTests { }); } + @Test + public void shouldSupportUnknownStatusCode() { + int errorStatus = 555; + assertNull(HttpStatus.resolve(errorStatus)); + String errorMessage = "Something went wrong"; + prepareResponse(response -> response.setResponseCode(errorStatus) + .setHeader("Content-Type", "text/plain").setBody(errorMessage)); + + Mono result = this.webClient.get() + .uri("/unknownPage") + .exchange(); + + StepVerifier.create(result) + .consumeNextWith(response -> assertEquals(555, response.rawStatusCode())) + .expectComplete() + .verify(Duration.ofSeconds(3)); + + expectRequestCount(1); + expectRequest(request -> { + assertEquals("*/*", request.getHeader(HttpHeaders.ACCEPT)); + assertEquals("/unknownPage", request.getPath()); + }); + } + + @Test + public void shouldGetErrorSignalWhenRetrievingUnknownStatusCode() { + int errorStatus = 555; + assertNull(HttpStatus.resolve(errorStatus)); + String errorMessage = "Something went wrong"; + prepareResponse(response -> response.setResponseCode(errorStatus) + .setHeader("Content-Type", "text/plain").setBody(errorMessage)); + + Mono result = this.webClient.get() + .uri("/unknownPage") + .retrieve() + .bodyToMono(String.class); + + StepVerifier.create(result) + .expectErrorSatisfies(throwable -> { + assertTrue(throwable instanceof UnknownHttpStatusCodeException); + UnknownHttpStatusCodeException ex = (UnknownHttpStatusCodeException) throwable; + assertEquals("Unknown status code ["+errorStatus+"]", ex.getMessage()); + assertEquals(errorStatus, ex.getRawStatusCode()); + assertEquals("", ex.getStatusText()); + assertEquals(MediaType.TEXT_PLAIN, ex.getHeaders().getContentType()); + assertEquals(errorMessage, ex.getResponseBodyAsString()); + }) + .verify(Duration.ofSeconds(3)); + + expectRequestCount(1); + expectRequest(request -> { + assertEquals("*/*", request.getHeader(HttpHeaders.ACCEPT)); + assertEquals("/unknownPage", request.getPath()); + }); + } + @Test public void shouldApplyCustomStatusHandler() { prepareResponse(response -> response.setResponseCode(500) diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/function/client/support/ClientResponseWrapperTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/function/client/support/ClientResponseWrapperTests.java index dad797715a9..fb9ba5dbf0f 100644 --- a/spring-webflux/src/test/java/org/springframework/web/reactive/function/client/support/ClientResponseWrapperTests.java +++ b/spring-webflux/src/test/java/org/springframework/web/reactive/function/client/support/ClientResponseWrapperTests.java @@ -65,6 +65,14 @@ public class ClientResponseWrapperTests { assertSame(status, wrapper.statusCode()); } + @Test + public void rawStatusCode() { + int status = 999; + when(mockResponse.rawStatusCode()).thenReturn(status); + + assertEquals(status, wrapper.rawStatusCode()); + } + @Test public void headers() { ClientResponse.Headers headers = mock(ClientResponse.Headers.class);