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:
@@ -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() {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user