Add client-side exception hierarchy

The `WebClient` has a new exception hierarchy:

* `WebClientException` is the root of all exceptions thrown by the
`WebClient`
* `WebClientResponseException` are all exceptions associated with
specific HTTP response status codes
* `WebClientErrorException` and `WebServerErrorException` are
respectively for 4xx and 5xx HTTP status codes

`ResponseExtractor` implementations are adapted to optionally throw
exceptions if it's impossible to extract the relevant parts of the
response (e.g. extracting the response body if the response is a 404).

This commit also introduces `ResponseErrorHandler`s that take care of
the whole exception mapping infrastructure. Since
`WebClientResponseException`s provide the status, headers and response
body, we also need a dedicated mechanism to extract information from the
response body at that level.

The `BodyExtractors` are responsible for extracting that information
from the exception, given they are provided with all the information
they need; in that case, message decoders are required.

To convey all this new information downstream, the `WebClient` now wraps
the message converters and response error handler instances into a
dedicated `WebClientConfig` object.
This commit is contained in:
Brian Clozel
2016-07-17 14:40:33 +02:00
parent fe79219607
commit 7a0c2422c6
20 changed files with 1056 additions and 194 deletions

View File

@@ -45,7 +45,7 @@ public class MockHttpOutputMessage implements HttpOutputMessage {
/**
* Return a copy of the actual headers written at the time of the call to
* getBody, i.e. ignoring any further changes that may have been made to
* getResponseBody, i.e. ignoring any further changes that may have been made to
* the underlying headers, e.g. via a previously obtained instance.
*/
public HttpHeaders getWrittenHeaders() {

View File

@@ -0,0 +1,99 @@
package org.springframework.web.client.reactive;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import static org.mockito.BDDMockito.*;
import static org.mockito.Mockito.mock;
import static org.springframework.web.client.reactive.ResponseExtractors.*;
import java.util.Collections;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import reactor.core.publisher.Flux;
import reactor.test.TestSubscriber;
import org.springframework.core.codec.StringDecoder;
import org.springframework.core.codec.StringEncoder;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.client.reactive.ClientHttpResponse;
import org.springframework.http.converter.reactive.CodecHttpMessageConverter;
import org.springframework.http.converter.reactive.HttpMessageConverter;
/**
* Unit tests for {@link DefaultResponseErrorHandler}.
*
* @author Brian Clozel
*/
public class DefaultResponseErrorHandlerTests {
private DefaultResponseErrorHandler errorHandler;
private ClientHttpResponse response;
private List<HttpMessageConverter<?>> messageConverters;
@Before
public void setUp() throws Exception {
this.errorHandler = new DefaultResponseErrorHandler();
this.response = mock(ClientHttpResponse.class);
this.messageConverters = Collections
.singletonList(new CodecHttpMessageConverter<>(new StringEncoder(), new StringDecoder()));
}
@Test
public void noError() throws Exception {
given(this.response.getStatusCode()).willReturn(HttpStatus.OK);
this.errorHandler.handleError(this.response, this.messageConverters);
}
@Test
public void clientError() throws Exception {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.TEXT_PLAIN);
DataBuffer buffer = new DefaultDataBufferFactory().allocateBuffer();
buffer.write(new String("Page Not Found").getBytes("UTF-8"));
given(this.response.getStatusCode()).willReturn(HttpStatus.NOT_FOUND);
given(this.response.getHeaders()).willReturn(headers);
given(this.response.getBody()).willReturn(Flux.just(buffer));
try {
this.errorHandler.handleError(this.response, this.messageConverters);
fail("expected HttpClientErrorException");
}
catch (WebClientErrorException exc) {
assertThat(exc.getMessage(), is("404 Not Found"));
assertThat(exc.getStatus(), is(HttpStatus.NOT_FOUND));
TestSubscriber.subscribe(exc.getResponseBody(as(String.class)))
.awaitAndAssertNextValues("Page Not Found")
.assertComplete();
}
}
@Test
public void serverError() throws Exception {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.TEXT_PLAIN);
DataBuffer buffer = new DefaultDataBufferFactory().allocateBuffer();
buffer.write(new String("Internal Server Error").getBytes("UTF-8"));
given(this.response.getStatusCode()).willReturn(HttpStatus.INTERNAL_SERVER_ERROR);
given(this.response.getHeaders()).willReturn(headers);
given(this.response.getBody()).willReturn(Flux.just(buffer));
try {
this.errorHandler.handleError(this.response, this.messageConverters);
fail("expected HttpServerErrorException");
}
catch (WebServerErrorException exc) {
assertThat(exc.getMessage(), is("500 Internal Server Error"));
assertThat(exc.getStatus(), is(HttpStatus.INTERNAL_SERVER_ERROR));
TestSubscriber.subscribe(exc.getResponseBody(as(String.class)))
.awaitAndAssertNextValues("Internal Server Error")
.assertComplete();
}
}
}

View File

@@ -0,0 +1,209 @@
package org.springframework.web.client.reactive;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import static org.mockito.BDDMockito.eq;
import static org.mockito.BDDMockito.*;
import static org.mockito.Mockito.mock;
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.test.TestSubscriber;
import reactor.core.Exceptions;
import org.springframework.core.codec.StringDecoder;
import org.springframework.core.codec.StringEncoder;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.reactive.ClientHttpResponse;
import org.springframework.http.codec.json.JacksonJsonDecoder;
import org.springframework.http.codec.json.JacksonJsonEncoder;
import org.springframework.http.converter.reactive.CodecHttpMessageConverter;
import org.springframework.http.converter.reactive.HttpMessageConverter;
/**
* Unit tests for {@link ResponseExtractors}.
*
* @author Brian Clozel
*/
public class ResponseExtractorsTests {
private HttpHeaders headers = new HttpHeaders();
private ClientHttpResponse response;
private List<HttpMessageConverter<?>> messageConverters;
private WebClientConfig webClientConfig;
private ResponseErrorHandler errorHandler;
@Before
public void setup() throws Exception {
this.headers = new HttpHeaders();
this.response = mock(ClientHttpResponse.class);
given(this.response.getHeaders()).willReturn(headers);
this.messageConverters = Arrays.asList(
new CodecHttpMessageConverter<>(new StringEncoder(), new StringDecoder()),
new CodecHttpMessageConverter<>(new JacksonJsonEncoder(), new JacksonJsonDecoder()));
this.webClientConfig = mock(WebClientConfig.class);
this.errorHandler = mock(ResponseErrorHandler.class);
given(this.webClientConfig.getMessageConverters()).willReturn(this.messageConverters);
given(this.webClientConfig.getResponseErrorHandler()).willReturn(this.errorHandler);
}
@Test
public void shouldExtractResponseEntityMono() throws Exception {
this.headers.setContentType(MediaType.TEXT_PLAIN);
given(this.response.getStatusCode()).willReturn(HttpStatus.OK);
given(this.response.getBody()).willReturn(createFluxBody("test content"));
Mono<ResponseEntity<String>> result = ResponseExtractors.response(String.class)
.extract(Mono.just(this.response), this.webClientConfig);
TestSubscriber.subscribe(result)
.awaitAndAssertNextValuesWith(entity -> {
assertThat(entity.getStatusCode(), is(HttpStatus.OK));
assertThat(entity.getHeaders().getContentType(), is(MediaType.TEXT_PLAIN));
assertThat(entity.getBody(), is("test content"));
})
.assertComplete();
}
@Test
public void shouldExtractResponseEntityFlux() throws Exception {
this.headers.setContentType(MediaType.TEXT_PLAIN);
given(this.response.getStatusCode()).willReturn(HttpStatus.OK);
given(this.response.getBody()).willReturn(createFluxBody("test", " content"));
Mono<ResponseEntity<String>> result = ResponseExtractors.response(String.class)
.extract(Mono.just(this.response), this.webClientConfig);
TestSubscriber.subscribe(result)
.awaitAndAssertNextValuesWith(entity -> {
assertThat(entity.getStatusCode(), is(HttpStatus.OK));
assertThat(entity.getHeaders().getContentType(), is(MediaType.TEXT_PLAIN));
assertThat(entity.getBody(), is("test content"));
})
.assertComplete();
}
@Test
public void shouldExtractResponseEntityWithEmptyBody() throws Exception {
given(this.response.getStatusCode()).willReturn(HttpStatus.NO_CONTENT);
given(this.response.getBody()).willReturn(Flux.empty());
Mono<ResponseEntity<String>> result = ResponseExtractors.response(String.class)
.extract(Mono.just(this.response), this.webClientConfig);
TestSubscriber.subscribe(result)
.awaitAndAssertNextValuesWith(entity -> {
assertThat(entity.getStatusCode(), is(HttpStatus.NO_CONTENT));
assertNull(entity.getBody());
})
.assertComplete();
}
@Test
public void shouldExtractResponseEntityAsStream() throws Exception {
this.headers.setContentType(MediaType.TEXT_PLAIN);
given(this.response.getStatusCode()).willReturn(HttpStatus.OK);
given(this.response.getBody()).willReturn(createFluxBody("test", " content"));
Mono<ResponseEntity<Flux<String>>> result = ResponseExtractors.responseStream(String.class)
.extract(Mono.just(this.response), this.webClientConfig);
TestSubscriber.subscribe(result)
.awaitAndAssertNextValuesWith(entity -> {
assertThat(entity.getStatusCode(), is(HttpStatus.OK));
assertThat(entity.getHeaders().getContentType(), is(MediaType.TEXT_PLAIN));
TestSubscriber.subscribe(entity.getBody())
.awaitAndAssertNextValues("test", " content")
.assertComplete();
})
.assertComplete();
}
@Test
public void shouldGetErrorWhenExtractingWithMissingConverter() throws Exception {
this.headers.setContentType(MediaType.APPLICATION_XML);
given(this.response.getStatusCode()).willReturn(HttpStatus.OK);
given(this.response.getBody()).willReturn(createFluxBody("test content"));
Mono<ResponseEntity<SomePojo>> result = ResponseExtractors.response(SomePojo.class)
.extract(Mono.just(this.response), this.webClientConfig);
TestSubscriber.subscribe(result)
.assertErrorWith(t -> {
assertThat(t, instanceOf(WebClientException.class));
WebClientException exc = (WebClientException) t;
assertThat(exc.getMessage(), containsString("Could not decode response body of type 'application/xml'"));
assertThat(exc.getMessage(), containsString("$SomePojo"));
});
}
@Test
public void shouldExtractResponseHeaders() throws Exception {
this.headers.setContentType(MediaType.TEXT_PLAIN);
this.headers.setETag("\"Spring\"");
given(this.response.getStatusCode()).willReturn(HttpStatus.OK);
Mono<HttpHeaders> result = ResponseExtractors.headers()
.extract(Mono.just(this.response), this.webClientConfig);
TestSubscriber.subscribe(result)
.awaitAndAssertNextValuesWith(headers -> {
assertThat(headers.getContentType(), is(MediaType.TEXT_PLAIN));
assertThat(headers.getETag(), is("\"Spring\""));
})
.assertComplete();
}
@Test
public void shouldExecuteResponseHandler() throws Exception {
this.headers.setContentType(MediaType.TEXT_PLAIN);
given(this.response.getStatusCode()).willReturn(HttpStatus.NOT_FOUND);
given(this.response.getBody()).willReturn(createFluxBody("test", " content"));
Mono<String> result = ResponseExtractors.body(String.class)
.extract(Mono.just(this.response), this.webClientConfig);
TestSubscriber.subscribe(result)
.assertValueCount(1)
.assertComplete();
then(this.errorHandler).should().handleError(eq(this.response), eq(this.messageConverters));
}
private Flux<DataBuffer> createFluxBody(String... items) throws Exception {
DefaultDataBufferFactory factory = new DefaultDataBufferFactory();
return Flux.just(items)
.map(item -> {
DataBuffer buffer = factory.allocateBuffer();
try {
buffer.write(new String(item).getBytes("UTF-8"));
}
catch (UnsupportedEncodingException exc) {
Exceptions.propagate(exc);
}
return buffer;
});
}
protected class SomePojo {
public String foo;
}
}

View File

@@ -28,9 +28,7 @@ import okhttp3.mockwebserver.MockWebServer;
import okhttp3.mockwebserver.RecordedRequest;
import org.hamcrest.Matchers;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import rx.Observable;
import rx.Single;
@@ -43,7 +41,7 @@ import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.http.codec.Pojo;
/**
* {@link WebClient} integration tests with the {@code Obserable} and {@code Single} API.
* {@link WebClient} integration tests with the {@code Observable} and {@code Single} API.
*
* @author Brian Clozel
*/
@@ -80,9 +78,9 @@ public class RxJava1WebClientIntegrationTests {
ts.assertCompleted();
RecordedRequest request = server.takeRequest();
Assert.assertEquals(1, server.getRequestCount());
Assert.assertEquals("*/*", request.getHeader(HttpHeaders.ACCEPT));
Assert.assertEquals("/greeting?name=Spring", request.getPath());
assertEquals(1, server.getRequestCount());
assertEquals("*/*", request.getHeader(HttpHeaders.ACCEPT));
assertEquals("/greeting?name=Spring", request.getPath());
}
@Test
@@ -106,10 +104,10 @@ public class RxJava1WebClientIntegrationTests {
ts.assertCompleted();
RecordedRequest request = server.takeRequest();
Assert.assertEquals(1, server.getRequestCount());
Assert.assertEquals("testvalue", request.getHeader("X-Test-Header"));
Assert.assertEquals("*/*", request.getHeader(HttpHeaders.ACCEPT));
Assert.assertEquals("/greeting?name=Spring", request.getPath());
assertEquals(1, server.getRequestCount());
assertEquals("testvalue", request.getHeader("X-Test-Header"));
assertEquals("*/*", request.getHeader(HttpHeaders.ACCEPT));
assertEquals("/greeting?name=Spring", request.getPath());
}
@Test
@@ -135,9 +133,9 @@ public class RxJava1WebClientIntegrationTests {
ts.assertCompleted();
RecordedRequest request = server.takeRequest();
Assert.assertEquals(1, server.getRequestCount());
Assert.assertEquals("/greeting?name=Spring", request.getPath());
Assert.assertEquals("text/plain", request.getHeader(HttpHeaders.ACCEPT));
assertEquals(1, server.getRequestCount());
assertEquals("/greeting?name=Spring", request.getPath());
assertEquals("text/plain", request.getHeader(HttpHeaders.ACCEPT));
}
@Test
@@ -163,9 +161,9 @@ public class RxJava1WebClientIntegrationTests {
ts.assertCompleted();
RecordedRequest request = server.takeRequest();
Assert.assertEquals(1, server.getRequestCount());
Assert.assertEquals("/json", request.getPath());
Assert.assertEquals("application/json", request.getHeader(HttpHeaders.ACCEPT));
assertEquals(1, server.getRequestCount());
assertEquals("/json", request.getPath());
assertEquals("application/json", request.getHeader(HttpHeaders.ACCEPT));
}
@Test
@@ -190,9 +188,9 @@ public class RxJava1WebClientIntegrationTests {
ts.assertCompleted();
RecordedRequest request = server.takeRequest();
Assert.assertEquals(1, server.getRequestCount());
Assert.assertEquals("/pojo", request.getPath());
Assert.assertEquals("application/json", request.getHeader(HttpHeaders.ACCEPT));
assertEquals(1, server.getRequestCount());
assertEquals("/pojo", request.getPath());
assertEquals("application/json", request.getHeader(HttpHeaders.ACCEPT));
}
@Test
@@ -217,9 +215,9 @@ public class RxJava1WebClientIntegrationTests {
ts.assertCompleted();
RecordedRequest request = server.takeRequest();
Assert.assertEquals(1, server.getRequestCount());
Assert.assertEquals("/pojos", request.getPath());
Assert.assertEquals("application/json", request.getHeader(HttpHeaders.ACCEPT));
assertEquals(1, server.getRequestCount());
assertEquals("/pojos", request.getPath());
assertEquals("application/json", request.getHeader(HttpHeaders.ACCEPT));
}
@Test
@@ -245,9 +243,9 @@ public class RxJava1WebClientIntegrationTests {
ts.assertCompleted();
RecordedRequest request = server.takeRequest();
Assert.assertEquals(1, server.getRequestCount());
Assert.assertEquals("/pojos", request.getPath());
Assert.assertEquals("application/json", request.getHeader(HttpHeaders.ACCEPT));
assertEquals(1, server.getRequestCount());
assertEquals("/pojos", request.getPath());
assertEquals("application/json", request.getHeader(HttpHeaders.ACCEPT));
}
@Test
@@ -275,12 +273,12 @@ public class RxJava1WebClientIntegrationTests {
ts.assertCompleted();
RecordedRequest request = server.takeRequest();
Assert.assertEquals(1, server.getRequestCount());
Assert.assertEquals("/pojo/capitalize", request.getPath());
Assert.assertEquals("{\"foo\":\"foofoo\",\"bar\":\"barbar\"}", request.getBody().readUtf8());
Assert.assertEquals("chunked", request.getHeader(HttpHeaders.TRANSFER_ENCODING));
Assert.assertEquals("application/json", request.getHeader(HttpHeaders.ACCEPT));
Assert.assertEquals("application/json", request.getHeader(HttpHeaders.CONTENT_TYPE));
assertEquals(1, server.getRequestCount());
assertEquals("/pojo/capitalize", request.getPath());
assertEquals("{\"foo\":\"foofoo\",\"bar\":\"barbar\"}", request.getBody().readUtf8());
assertEquals("chunked", request.getHeader(HttpHeaders.TRANSFER_ENCODING));
assertEquals("application/json", request.getHeader(HttpHeaders.ACCEPT));
assertEquals("application/json", request.getHeader(HttpHeaders.CONTENT_TYPE));
}
@Test
@@ -304,33 +302,35 @@ public class RxJava1WebClientIntegrationTests {
ts.assertCompleted();
RecordedRequest request = server.takeRequest();
Assert.assertEquals(1, server.getRequestCount());
Assert.assertEquals("/test", request.getPath());
Assert.assertEquals("testkey=testvalue", request.getHeader(HttpHeaders.COOKIE));
assertEquals(1, server.getRequestCount());
assertEquals("/test", request.getPath());
assertEquals("testkey=testvalue", request.getHeader(HttpHeaders.COOKIE));
}
@Test
@Ignore
public void shouldGetErrorWhen404() throws Exception {
HttpUrl baseUrl = server.url("/greeting?name=Spring");
this.server.enqueue(new MockResponse().setResponseCode(404));
this.server.enqueue(new MockResponse().setResponseCode(404)
.setHeader("Content-Type", "text/plain").setBody("Not Found"));
Single<String> result = this.webClient
.perform(get(baseUrl.toString()))
.extract(body(String.class));
// TODO: error message should be converted to a ClientException
TestSubscriber<String> ts = new TestSubscriber<String>();
result.subscribe(ts);
ts.awaitTerminalEvent(2, TimeUnit.SECONDS);
ts.assertError(WebClientException.class);
ts.assertError(WebClientErrorException.class);
WebClientErrorException exc = (WebClientErrorException) ts.getOnErrorEvents().get(0);
assertEquals(404, exc.getStatus().value());
assertEquals(MediaType.TEXT_PLAIN, exc.getResponseHeaders().getContentType());
RecordedRequest request = server.takeRequest();
Assert.assertEquals(1, server.getRequestCount());
Assert.assertEquals("*/*", request.getHeader(HttpHeaders.ACCEPT));
Assert.assertEquals("/greeting?name=Spring", request.getPath());
assertEquals(1, server.getRequestCount());
assertEquals("*/*", request.getHeader(HttpHeaders.ACCEPT));
assertEquals("/greeting?name=Spring", request.getPath());
}
@After

View File

@@ -28,18 +28,17 @@ import okhttp3.mockwebserver.MockWebServer;
import okhttp3.mockwebserver.RecordedRequest;
import org.hamcrest.Matchers;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.test.TestSubscriber;
import org.springframework.http.codec.Pojo;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.http.codec.Pojo;
/**
* {@link WebClient} integration tests with the {@code Flux} and {@code Mono} API.
@@ -71,16 +70,16 @@ public class WebClientIntegrationTests {
TestSubscriber
.subscribe(result)
.awaitAndAssertNextValuesWith(
httpHeaders -> {
assertEquals(MediaType.TEXT_PLAIN, httpHeaders.getContentType());
assertEquals(13L, httpHeaders.getContentLength());
})
httpHeaders -> {
assertEquals(MediaType.TEXT_PLAIN, httpHeaders.getContentType());
assertEquals(13L, httpHeaders.getContentLength());
})
.assertComplete();
RecordedRequest request = server.takeRequest();
Assert.assertEquals(1, server.getRequestCount());
Assert.assertEquals("*/*", request.getHeader(HttpHeaders.ACCEPT));
Assert.assertEquals("/greeting?name=Spring", request.getPath());
assertEquals(1, server.getRequestCount());
assertEquals("*/*", request.getHeader(HttpHeaders.ACCEPT));
assertEquals("/greeting?name=Spring", request.getPath());
}
@Test
@@ -101,10 +100,10 @@ public class WebClientIntegrationTests {
.assertComplete();
RecordedRequest request = server.takeRequest();
Assert.assertEquals(1, server.getRequestCount());
Assert.assertEquals("testvalue", request.getHeader("X-Test-Header"));
Assert.assertEquals("*/*", request.getHeader(HttpHeaders.ACCEPT));
Assert.assertEquals("/greeting?name=Spring", request.getPath());
assertEquals(1, server.getRequestCount());
assertEquals("testvalue", request.getHeader("X-Test-Header"));
assertEquals("*/*", request.getHeader(HttpHeaders.ACCEPT));
assertEquals("/greeting?name=Spring", request.getPath());
}
@Test
@@ -124,11 +123,11 @@ public class WebClientIntegrationTests {
assertEquals(200, response.getStatusCode().value());
assertEquals(MediaType.TEXT_PLAIN, response.getHeaders().getContentType());
assertEquals("Hello Spring!", response.getBody());
});
});
RecordedRequest request = server.takeRequest();
Assert.assertEquals(1, server.getRequestCount());
Assert.assertEquals("/greeting?name=Spring", request.getPath());
Assert.assertEquals("text/plain", request.getHeader(HttpHeaders.ACCEPT));
assertEquals(1, server.getRequestCount());
assertEquals("/greeting?name=Spring", request.getPath());
assertEquals("text/plain", request.getHeader(HttpHeaders.ACCEPT));
}
@Test
@@ -149,9 +148,9 @@ public class WebClientIntegrationTests {
.awaitAndAssertNextValues(content)
.assertComplete();
RecordedRequest request = server.takeRequest();
Assert.assertEquals(1, server.getRequestCount());
Assert.assertEquals("/json", request.getPath());
Assert.assertEquals("application/json", request.getHeader(HttpHeaders.ACCEPT));
assertEquals(1, server.getRequestCount());
assertEquals("/json", request.getPath());
assertEquals("application/json", request.getHeader(HttpHeaders.ACCEPT));
}
@Test
@@ -171,9 +170,9 @@ public class WebClientIntegrationTests {
.awaitAndAssertNextValuesWith(p -> assertEquals("barbar", p.getBar()))
.assertComplete();
RecordedRequest request = server.takeRequest();
Assert.assertEquals(1, server.getRequestCount());
Assert.assertEquals("/pojo", request.getPath());
Assert.assertEquals("application/json", request.getHeader(HttpHeaders.ACCEPT));
assertEquals(1, server.getRequestCount());
assertEquals("/pojo", request.getPath());
assertEquals("application/json", request.getHeader(HttpHeaders.ACCEPT));
}
@Test
@@ -191,14 +190,14 @@ public class WebClientIntegrationTests {
TestSubscriber
.subscribe(result)
.awaitAndAssertNextValuesWith(
p -> assertThat(p.getBar(), Matchers.is("bar1")),
p -> assertThat(p.getBar(), Matchers.is("bar2")))
p -> assertThat(p.getBar(), Matchers.is("bar1")),
p -> assertThat(p.getBar(), Matchers.is("bar2")))
.assertValueCount(2)
.assertComplete();
RecordedRequest request = server.takeRequest();
Assert.assertEquals(1, server.getRequestCount());
Assert.assertEquals("/pojos", request.getPath());
Assert.assertEquals("application/json", request.getHeader(HttpHeaders.ACCEPT));
assertEquals(1, server.getRequestCount());
assertEquals("/pojos", request.getPath());
assertEquals("application/json", request.getHeader(HttpHeaders.ACCEPT));
}
@Test
@@ -216,15 +215,15 @@ public class WebClientIntegrationTests {
TestSubscriber
.subscribe(result)
.awaitAndAssertNextValuesWith(
response -> {
assertEquals(200, response.getStatusCode().value());
assertEquals(MediaType.APPLICATION_JSON, response.getHeaders().getContentType());
})
response -> {
assertEquals(200, response.getStatusCode().value());
assertEquals(MediaType.APPLICATION_JSON, response.getHeaders().getContentType());
})
.assertComplete();
RecordedRequest request = server.takeRequest();
Assert.assertEquals(1, server.getRequestCount());
Assert.assertEquals("/pojos", request.getPath());
Assert.assertEquals("application/json", request.getHeader(HttpHeaders.ACCEPT));
assertEquals(1, server.getRequestCount());
assertEquals("/pojos", request.getPath());
assertEquals("application/json", request.getHeader(HttpHeaders.ACCEPT));
}
@Test
@@ -249,12 +248,12 @@ public class WebClientIntegrationTests {
.assertComplete();
RecordedRequest request = server.takeRequest();
Assert.assertEquals(1, server.getRequestCount());
Assert.assertEquals("/pojo/capitalize", request.getPath());
Assert.assertEquals("{\"foo\":\"foofoo\",\"bar\":\"barbar\"}", request.getBody().readUtf8());
Assert.assertEquals("chunked", request.getHeader(HttpHeaders.TRANSFER_ENCODING));
Assert.assertEquals("application/json", request.getHeader(HttpHeaders.ACCEPT));
Assert.assertEquals("application/json", request.getHeader(HttpHeaders.CONTENT_TYPE));
assertEquals(1, server.getRequestCount());
assertEquals("/pojo/capitalize", request.getPath());
assertEquals("{\"foo\":\"foofoo\",\"bar\":\"barbar\"}", request.getBody().readUtf8());
assertEquals("chunked", request.getHeader(HttpHeaders.TRANSFER_ENCODING));
assertEquals("application/json", request.getHeader(HttpHeaders.ACCEPT));
assertEquals("application/json", request.getHeader(HttpHeaders.CONTENT_TYPE));
}
@Test
@@ -274,16 +273,17 @@ public class WebClientIntegrationTests {
.assertComplete();
RecordedRequest request = server.takeRequest();
Assert.assertEquals(1, server.getRequestCount());
Assert.assertEquals("/test", request.getPath());
Assert.assertEquals("testkey=testvalue", request.getHeader(HttpHeaders.COOKIE));
assertEquals(1, server.getRequestCount());
assertEquals("/test", request.getPath());
assertEquals("testkey=testvalue", request.getHeader(HttpHeaders.COOKIE));
}
@Test
public void shouldGetErrorWhen404() throws Exception {
HttpUrl baseUrl = server.url("/greeting?name=Spring");
this.server.enqueue(new MockResponse().setResponseCode(404));
this.server.enqueue(new MockResponse().setResponseCode(404)
.setHeader("Content-Type", "text/plain").setBody("Not Found"));
Mono<String> result = this.webClient
.perform(get(baseUrl.toString()))
@@ -292,12 +292,50 @@ public class WebClientIntegrationTests {
TestSubscriber
.subscribe(result)
.await()
.assertError();
.assertErrorWith(t -> {
assertThat(t, Matchers.instanceOf(WebClientErrorException.class));
WebClientErrorException exc = (WebClientErrorException) t;
assertEquals(404, exc.getStatus().value());
assertEquals(MediaType.TEXT_PLAIN, exc.getResponseHeaders().getContentType());
Mono<String> body = exc.getResponseBody(as(String.class));
TestSubscriber.subscribe(body)
.awaitAndAssertNextValues("Not Found")
.assertComplete();
});
RecordedRequest request = server.takeRequest();
Assert.assertEquals(1, server.getRequestCount());
Assert.assertEquals("*/*", request.getHeader(HttpHeaders.ACCEPT));
Assert.assertEquals("/greeting?name=Spring", request.getPath());
assertEquals(1, server.getRequestCount());
assertEquals("*/*", request.getHeader(HttpHeaders.ACCEPT));
assertEquals("/greeting?name=Spring", request.getPath());
}
@Test
public void shouldGetErrorWhen500() throws Exception {
HttpUrl baseUrl = server.url("/greeting?name=Spring");
this.server.enqueue(new MockResponse().setResponseCode(500)
.setHeader("Content-Type", "text/plain").setBody("Server Error"));
Mono<String> result = this.webClient
.perform(get(baseUrl.toString()))
.extract(body(String.class));
TestSubscriber
.subscribe(result)
.await()
.assertErrorWith(t -> {
assertThat(t, Matchers.instanceOf(WebServerErrorException.class));
WebServerErrorException exc = (WebServerErrorException) t;
assertEquals(500, exc.getStatus().value());
assertEquals(MediaType.TEXT_PLAIN, exc.getResponseHeaders().getContentType());
});
RecordedRequest request = server.takeRequest();
assertEquals(1, server.getRequestCount());
assertEquals("*/*", request.getHeader(HttpHeaders.ACCEPT));
assertEquals("/greeting?name=Spring", request.getPath());
}
@After