Improve WebClient error handling

This commit introduces two new `WebClient` methods: `retrieveMono` and
`retrieveFlux`, both of which offer direct access to the response body.
More importantly, these methods publish a WebClientException if the
response status code is in the 4xx or 5xx series.

Issue: SPR-14852
This commit is contained in:
Arjen Poutsma
2016-10-28 11:39:28 +02:00
parent c96badc794
commit 7b469f9c62
6 changed files with 386 additions and 32 deletions

View File

@@ -16,15 +16,20 @@
package org.springframework.web.client.reactive;
import java.util.Optional;
import org.junit.Test;
import reactor.core.publisher.Mono;
import reactor.test.subscriber.ScriptedSubscriber;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
/**
* @author Arjen Poutsma
@@ -80,6 +85,75 @@ public class ExchangeFilterFunctionsTests {
assertTrue(filterInvoked[0]);
}
@Test
public void clientNoError() throws Exception {
ClientRequest<Void> request = ClientRequest.GET("http://example.com").build();
ClientResponse response = mock(ClientResponse.class);
when(response.statusCode()).thenReturn(HttpStatus.OK);
ExchangeFunction exchange = r -> Mono.just(response);
ExchangeFilterFunction standardErrors = ExchangeFilterFunctions.clientError();
Mono<ClientResponse> result = standardErrors.filter(request, exchange);
ScriptedSubscriber.<ClientResponse>create()
.expectNext(response)
.expectComplete()
.verify(result);
}
@Test
public void serverError() throws Exception {
ClientRequest<Void> request = ClientRequest.GET("http://example.com").build();
ClientResponse response = mock(ClientResponse.class);
when(response.statusCode()).thenReturn(HttpStatus.INTERNAL_SERVER_ERROR);
ExchangeFunction exchange = r -> Mono.just(response);
ExchangeFilterFunction standardErrors = ExchangeFilterFunctions.serverError();
Mono<ClientResponse> result = standardErrors.filter(request, exchange);
ScriptedSubscriber.<ClientResponse>create()
.expectError(WebClientException.class)
.verify(result);
}
@Test
public void errorPredicate() throws Exception {
ClientRequest<Void> request = ClientRequest.GET("http://example.com").build();
ClientResponse response = mock(ClientResponse.class);
when(response.statusCode()).thenReturn(HttpStatus.NOT_FOUND);
ExchangeFunction exchange = r -> Mono.just(response);
ExchangeFilterFunction errorPredicate = ExchangeFilterFunctions
.errorPredicate(clientResponse -> clientResponse.statusCode().is4xxClientError());
Mono<ClientResponse> result = errorPredicate.filter(request, exchange);
ScriptedSubscriber.<ClientResponse>create()
.expectError(WebClientException.class)
.verify(result);
}
@Test
public void errorMapperFunction() throws Exception {
ClientRequest<Void> request = ClientRequest.GET("http://example.com").build();
ClientResponse response = mock(ClientResponse.class);
ExchangeFunction exchange = r -> Mono.just(response);
ExchangeFilterFunction errorMapper = ExchangeFilterFunctions
.errorMapper(clientResponse -> Optional.of(new IllegalStateException()));
Mono<ClientResponse> result = errorMapper.filter(request, exchange);
ScriptedSubscriber.<ClientResponse>create()
.expectError(IllegalStateException.class)
.verify(result);
}
@Test
public void basicAuthentication() throws Exception {
ClientRequest<Void> request = ClientRequest.GET("http://example.com").build();

View File

@@ -111,6 +111,50 @@ public class WebClientIntegrationTests {
assertEquals("/greeting?name=Spring", recordedRequest.getPath());
}
@Test
public void retrieveMono() throws Exception {
HttpUrl baseUrl = server.url("/greeting?name=Spring");
this.server.enqueue(new MockResponse().setBody("Hello Spring!"));
ClientRequest<Void> request = ClientRequest.GET(baseUrl.toString()).build();
Mono<String> result = this.webClient
.retrieveMono(request, String.class);
ScriptedSubscriber
.<String>create()
.expectNext("Hello Spring!")
.expectComplete()
.verify(result);
RecordedRequest recordedRequest = server.takeRequest();
assertEquals(1, server.getRequestCount());
assertEquals("*/*", recordedRequest.getHeader(HttpHeaders.ACCEPT));
assertEquals("/greeting?name=Spring", recordedRequest.getPath());
}
@Test
public void retrieveFlux() throws Exception {
HttpUrl baseUrl = server.url("/greeting?name=Spring");
this.server.enqueue(new MockResponse().setBody("Hello Spring!"));
ClientRequest<Void> request = ClientRequest.GET(baseUrl.toString()).build();
Flux<String> result = this.webClient
.retrieveFlux(request, String.class);
ScriptedSubscriber
.<String>create()
.expectNext("Hello Spring!")
.expectComplete()
.verify(result);
RecordedRequest recordedRequest = server.takeRequest();
assertEquals(1, server.getRequestCount());
assertEquals("*/*", recordedRequest.getHeader(HttpHeaders.ACCEPT));
assertEquals("/greeting?name=Spring", recordedRequest.getPath());
}
@Test
public void jsonString() throws Exception {
HttpUrl baseUrl = server.url("/json");
@@ -274,6 +318,50 @@ public class WebClientIntegrationTests {
assertEquals("/greeting?name=Spring", recordedRequest.getPath());
}
@Test
public void retrieveNotFound() throws Exception {
HttpUrl baseUrl = server.url("/greeting?name=Spring");
this.server.enqueue(new MockResponse().setResponseCode(404)
.setHeader("Content-Type", "text/plain").setBody("Not Found"));
ClientRequest<Void> request = ClientRequest.GET(baseUrl.toString()).build();
Mono<String> result = this.webClient
.retrieveMono(request, String.class);
ScriptedSubscriber
.<String>create()
.expectError(WebClientException.class)
.verify(result, Duration.ofSeconds(3));
RecordedRequest recordedRequest = server.takeRequest();
assertEquals(1, server.getRequestCount());
assertEquals("*/*", recordedRequest.getHeader(HttpHeaders.ACCEPT));
assertEquals("/greeting?name=Spring", recordedRequest.getPath());
}
@Test
public void retrieveServerError() throws Exception {
HttpUrl baseUrl = server.url("/greeting?name=Spring");
this.server.enqueue(new MockResponse().setResponseCode(500)
.setHeader("Content-Type", "text/plain").setBody("Not Found"));
ClientRequest<Void> request = ClientRequest.GET(baseUrl.toString()).build();
Mono<String> result = this.webClient
.retrieveMono(request, String.class);
ScriptedSubscriber
.<String>create()
.expectError(WebClientException.class)
.verify(result, Duration.ofSeconds(3));
RecordedRequest recordedRequest = server.takeRequest();
assertEquals(1, server.getRequestCount());
assertEquals("*/*", recordedRequest.getHeader(HttpHeaders.ACCEPT));
assertEquals("/greeting?name=Spring", recordedRequest.getPath());
}
@Test
public void filter() throws Exception {
HttpUrl baseUrl = server.url("/greeting?name=Spring");