Move WebClient to web.reactive.function.client

This commit is contained in:
Arjen Poutsma
2016-12-15 12:14:15 +01:00
parent dfcd5b9ed9
commit aa8f531526
22 changed files with 99 additions and 61 deletions

View File

@@ -1,138 +0,0 @@
/*
* Copyright 2002-2016 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.http.server.reactive;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import org.junit.Before;
import org.junit.Test;
import org.reactivestreams.Publisher;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.http.codec.BodyExtractors;
import org.springframework.util.Assert;
import org.springframework.web.client.reactive.ClientRequest;
import org.springframework.web.client.reactive.WebClient;
/**
* @author Sebastien Deleuze
*/
public class FlushingIntegrationTests extends AbstractHttpHandlerIntegrationTests {
private WebClient webClient;
@Before
public void setup() throws Exception {
super.setup();
this.webClient = WebClient.create(new ReactorClientHttpConnector());
}
@Test
public void writeAndFlushWith() throws Exception {
ClientRequest<Void> request = ClientRequest.GET("http://localhost:" + port + "/write-and-flush").build();
Mono<String> result = this.webClient
.exchange(request)
.flatMap(response -> response.body(BodyExtractors.toFlux(String.class)))
.takeUntil(s -> s.endsWith("data1"))
.reduce((s1, s2) -> s1 + s2);
StepVerifier.create(result)
.expectNext("data0data1")
.expectComplete()
.verify(Duration.ofSeconds(5L));
}
@Test // SPR-14991
public void writeAndAutoFlushOnComplete() {
ClientRequest<Void> request = ClientRequest.GET("http://localhost:" + port + "/write-and-complete").build();
Mono<String> result = this.webClient
.exchange(request)
.flatMap(response -> response.bodyToFlux(String.class))
.reduce((s1, s2) -> s1 + s2);
StepVerifier.create(result)
.consumeNextWith(value -> Assert.isTrue(value.length() == 200000))
.expectComplete()
.verify(Duration.ofSeconds(5L));
}
@Test // SPR-14992
public void writeAndAutoFlushBeforeComplete() {
ClientRequest<Void> request = ClientRequest.GET("http://localhost:" + port + "/write-and-never-complete").build();
Flux<String> result = this.webClient
.exchange(request)
.flatMap(response -> response.bodyToFlux(String.class));
StepVerifier.create(result)
.expectNextMatches(s -> s.startsWith("0123456789"))
.thenCancel()
.verify(Duration.ofSeconds(5L));
}
@Override
protected HttpHandler createHttpHandler() {
return new FlushingHandler();
}
private static class FlushingHandler implements HttpHandler {
@Override
public Mono<Void> handle(ServerHttpRequest request, ServerHttpResponse response) {
String path = request.getURI().getPath();
if (path.endsWith("write-and-flush")) {
Flux<Publisher<DataBuffer>> responseBody = Flux
.intervalMillis(50)
.map(l -> toDataBuffer("data" + l, response.bufferFactory()))
.take(2)
.map(Flux::just);
responseBody = responseBody.concatWith(Flux.never());
return response.writeAndFlushWith(responseBody);
}
else if (path.endsWith("write-and-complete")) {
Flux<DataBuffer> responseBody = Flux
.just("0123456789")
.repeat(20000)
.map(value -> toDataBuffer(value, response.bufferFactory()));
return response.writeWith(responseBody);
}
else if (path.endsWith("write-and-never-complete")) {
Flux<DataBuffer> responseBody = Flux
.just("0123456789")
.repeat(20000)
.map(value -> toDataBuffer(value, response.bufferFactory()))
.mergeWith(Flux.never());
return response.writeWith(responseBody);
}
return response.writeWith(Flux.empty());
}
private DataBuffer toDataBuffer(String value, DataBufferFactory factory) {
byte[] data = (value).getBytes(StandardCharsets.UTF_8);
DataBuffer buffer = factory.allocateBuffer(data.length);
buffer.write(data);
return buffer;
}
}
}

View File

@@ -1,211 +0,0 @@
/*
* Copyright 2002-2016 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.client.reactive;
import java.net.URI;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.junit.Test;
import reactor.core.publisher.Mono;
import org.springframework.core.codec.CharSequenceEncoder;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.client.reactive.ClientHttpRequest;
import org.springframework.http.codec.BodyInserter;
import org.springframework.http.codec.EncoderHttpMessageWriter;
import org.springframework.http.codec.HttpMessageWriter;
import org.springframework.web.client.reactive.test.MockClientHttpRequest;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
/**
* @author Arjen Poutsma
*/
public class DefaultClientRequestBuilderTests {
@Test
public void from() throws Exception {
ClientRequest<Void> other = ClientRequest.GET("http://example.com")
.header("foo", "bar")
.cookie("baz", "qux").build();
ClientRequest<Void> result = ClientRequest.from(other).build();
assertEquals(new URI("http://example.com"), result.url());
assertEquals(HttpMethod.GET, result.method());
assertEquals("bar", result.headers().getFirst("foo"));
assertEquals("qux", result.cookies().getFirst("baz"));
}
@Test
public void method() throws Exception {
URI url = new URI("http://example.com");
ClientRequest<Void> result = ClientRequest.method(HttpMethod.DELETE, url).build();
assertEquals(url, result.url());
assertEquals(HttpMethod.DELETE, result.method());
}
@Test
public void GET() throws Exception {
URI url = new URI("http://example.com");
ClientRequest<Void> result = ClientRequest.GET(url.toString()).build();
assertEquals(url, result.url());
assertEquals(HttpMethod.GET, result.method());
}
@Test
public void HEAD() throws Exception {
URI url = new URI("http://example.com");
ClientRequest<Void> result = ClientRequest.HEAD(url.toString()).build();
assertEquals(url, result.url());
assertEquals(HttpMethod.HEAD, result.method());
}
@Test
public void POST() throws Exception {
URI url = new URI("http://example.com");
ClientRequest<Void> result = ClientRequest.POST(url.toString()).build();
assertEquals(url, result.url());
assertEquals(HttpMethod.POST, result.method());
}
@Test
public void PUT() throws Exception {
URI url = new URI("http://example.com");
ClientRequest<Void> result = ClientRequest.PUT(url.toString()).build();
assertEquals(url, result.url());
assertEquals(HttpMethod.PUT, result.method());
}
@Test
public void PATCH() throws Exception {
URI url = new URI("http://example.com");
ClientRequest<Void> result = ClientRequest.PATCH(url.toString()).build();
assertEquals(url, result.url());
assertEquals(HttpMethod.PATCH, result.method());
}
@Test
public void DELETE() throws Exception {
URI url = new URI("http://example.com");
ClientRequest<Void> result = ClientRequest.DELETE(url.toString()).build();
assertEquals(url, result.url());
assertEquals(HttpMethod.DELETE, result.method());
}
@Test
public void OPTIONS() throws Exception {
URI url = new URI("http://example.com");
ClientRequest<Void> result = ClientRequest.OPTIONS(url.toString()).build();
assertEquals(url, result.url());
assertEquals(HttpMethod.OPTIONS, result.method());
}
@Test
public void accept() throws Exception {
MediaType json = MediaType.APPLICATION_JSON;
ClientRequest<Void> result = ClientRequest.GET("http://example.com").accept(json).build();
assertEquals(Collections.singletonList(json), result.headers().getAccept());
}
@Test
public void acceptCharset() throws Exception {
Charset charset = Charset.defaultCharset();
ClientRequest<Void> result = ClientRequest.GET("http://example.com")
.acceptCharset(charset).build();
assertEquals(Collections.singletonList(charset), result.headers().getAcceptCharset());
}
@Test
public void ifModifiedSince() throws Exception {
ZonedDateTime now = ZonedDateTime.now();
ClientRequest<Void> result = ClientRequest.GET("http://example.com")
.ifModifiedSince(now).build();
assertEquals(now.toInstant().toEpochMilli()/1000, result.headers().getIfModifiedSince()/1000);
}
@Test
public void ifNoneMatch() throws Exception {
ClientRequest<Void> result = ClientRequest.GET("http://example.com")
.ifNoneMatch("\"v2.7\"", "\"v2.8\"").build();
assertEquals(Arrays.asList("\"v2.7\"", "\"v2.8\""), result.headers().getIfNoneMatch());
}
@Test
public void cookie() throws Exception {
ClientRequest<Void> result = ClientRequest.GET("http://example.com")
.cookie("foo", "bar").build();
assertEquals("bar", result.cookies().getFirst("foo"));
}
@Test
public void build() throws Exception {
ClientRequest<Void> result = ClientRequest.GET("http://example.com")
.header("MyKey", "MyValue")
.cookie("foo", "bar")
.build();
MockClientHttpRequest request = new MockClientHttpRequest();
WebClientStrategies strategies = mock(WebClientStrategies.class);
result.writeTo(request, strategies).block();
assertEquals("MyValue", request.getHeaders().getFirst("MyKey"));
assertEquals("bar", request.getCookies().getFirst("foo").getValue());
assertNull(request.getBody());
}
@Test
public void bodyInserter() throws Exception {
String body = "foo";
BodyInserter<String, ClientHttpRequest> inserter =
(response, strategies) -> {
byte[] bodyBytes = body.getBytes(UTF_8);
ByteBuffer byteBuffer = ByteBuffer.wrap(bodyBytes);
DataBuffer buffer = new DefaultDataBufferFactory().wrap(byteBuffer);
return response.writeWith(Mono.just(buffer));
};
ClientRequest<String> result = ClientRequest.POST("http://example.com")
.body(inserter);
MockClientHttpRequest request = new MockClientHttpRequest();
List<HttpMessageWriter<?>> messageWriters = new ArrayList<>();
messageWriters.add(new EncoderHttpMessageWriter<CharSequence>(new CharSequenceEncoder()));
WebClientStrategies strategies = mock(WebClientStrategies.class);
when(strategies.messageWriters()).thenReturn(messageWriters::stream);
result.writeTo(request, strategies).block();
assertNotNull(request.getBody());
}
}

View File

@@ -1,198 +0,0 @@
/*
* Copyright 2002-2016 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.client.reactive;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.OptionalLong;
import java.util.Set;
import org.junit.Before;
import org.junit.Test;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import org.springframework.core.codec.StringDecoder;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DefaultDataBuffer;
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpRange;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.client.reactive.ClientHttpResponse;
import org.springframework.http.codec.DecoderHttpMessageReader;
import org.springframework.http.codec.HttpMessageReader;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.springframework.http.codec.BodyExtractors.toMono;
/**
* @author Arjen Poutsma
*/
public class DefaultClientResponseTests {
private ClientHttpResponse mockResponse;
private WebClientStrategies mockWebClientStrategies;
private DefaultClientResponse defaultClientResponse;
@Before
public void createMocks() {
mockResponse = mock(ClientHttpResponse.class);
mockWebClientStrategies = mock(WebClientStrategies.class);
defaultClientResponse = new DefaultClientResponse(mockResponse, mockWebClientStrategies);
}
@Test
public void statusCode() throws Exception {
HttpStatus status = HttpStatus.CONTINUE;
when(mockResponse.getStatusCode()).thenReturn(status);
assertEquals(status, defaultClientResponse.statusCode());
}
@Test
public void header() throws Exception {
HttpHeaders httpHeaders = new HttpHeaders();
long contentLength = 42L;
httpHeaders.setContentLength(contentLength);
MediaType contentType = MediaType.TEXT_PLAIN;
httpHeaders.setContentType(contentType);
InetSocketAddress host = InetSocketAddress.createUnresolved("localhost", 80);
httpHeaders.setHost(host);
List<HttpRange> range = Collections.singletonList(HttpRange.createByteRange(0, 42));
httpHeaders.setRange(range);
when(mockResponse.getHeaders()).thenReturn(httpHeaders);
ClientResponse.Headers headers = defaultClientResponse.headers();
assertEquals(OptionalLong.of(contentLength), headers.contentLength());
assertEquals(Optional.of(contentType), headers.contentType());
assertEquals(httpHeaders, headers.asHttpHeaders());
}
@Test
public void body() throws Exception {
DefaultDataBufferFactory factory = new DefaultDataBufferFactory();
DefaultDataBuffer dataBuffer =
factory.wrap(ByteBuffer.wrap("foo".getBytes(StandardCharsets.UTF_8)));
Flux<DataBuffer> body = Flux.just(dataBuffer);
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.TEXT_PLAIN);
when(mockResponse.getHeaders()).thenReturn(httpHeaders);
when(mockResponse.getBody()).thenReturn(body);
Set<HttpMessageReader<?>> messageReaders = Collections
.singleton(new DecoderHttpMessageReader<String>(new StringDecoder()));
when(mockWebClientStrategies.messageReaders()).thenReturn(messageReaders::stream);
Mono<String> resultMono = defaultClientResponse.body(toMono(String.class));
assertEquals("foo", resultMono.block());
}
@Test
public void bodyToMono() throws Exception {
DefaultDataBufferFactory factory = new DefaultDataBufferFactory();
DefaultDataBuffer dataBuffer =
factory.wrap(ByteBuffer.wrap("foo".getBytes(StandardCharsets.UTF_8)));
Flux<DataBuffer> body = Flux.just(dataBuffer);
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.TEXT_PLAIN);
when(mockResponse.getHeaders()).thenReturn(httpHeaders);
when(mockResponse.getStatusCode()).thenReturn(HttpStatus.OK);
when(mockResponse.getBody()).thenReturn(body);
Set<HttpMessageReader<?>> messageReaders = Collections
.singleton(new DecoderHttpMessageReader<String>(new StringDecoder()));
when(mockWebClientStrategies.messageReaders()).thenReturn(messageReaders::stream);
Mono<String> resultMono = defaultClientResponse.bodyToMono(String.class);
assertEquals("foo", resultMono.block());
}
@Test
public void bodyToMonoError() throws Exception {
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.TEXT_PLAIN);
when(mockResponse.getHeaders()).thenReturn(httpHeaders);
when(mockResponse.getStatusCode()).thenReturn(HttpStatus.NOT_FOUND);
Set<HttpMessageReader<?>> messageReaders = Collections
.singleton(new DecoderHttpMessageReader<String>(new StringDecoder()));
when(mockWebClientStrategies.messageReaders()).thenReturn(messageReaders::stream);
Mono<String> resultMono = defaultClientResponse.bodyToMono(String.class);
StepVerifier.create(resultMono)
.expectError(WebClientException.class)
.verify();
}
@Test
public void bodyToFlux() throws Exception {
DefaultDataBufferFactory factory = new DefaultDataBufferFactory();
DefaultDataBuffer dataBuffer =
factory.wrap(ByteBuffer.wrap("foo".getBytes(StandardCharsets.UTF_8)));
Flux<DataBuffer> body = Flux.just(dataBuffer);
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.TEXT_PLAIN);
when(mockResponse.getHeaders()).thenReturn(httpHeaders);
when(mockResponse.getStatusCode()).thenReturn(HttpStatus.OK);
when(mockResponse.getBody()).thenReturn(body);
Set<HttpMessageReader<?>> messageReaders = Collections
.singleton(new DecoderHttpMessageReader<String>(new StringDecoder()));
when(mockWebClientStrategies.messageReaders()).thenReturn(messageReaders::stream);
Flux<String> resultFlux = defaultClientResponse.bodyToFlux(String.class);
Mono<List<String>> result = resultFlux.collectList();
assertEquals(Collections.singletonList("foo"), result.block());
}
@Test
public void bodyToFluxError() throws Exception {
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.TEXT_PLAIN);
when(mockResponse.getHeaders()).thenReturn(httpHeaders);
when(mockResponse.getStatusCode()).thenReturn(HttpStatus.INTERNAL_SERVER_ERROR);
Set<HttpMessageReader<?>> messageReaders = Collections
.singleton(new DecoderHttpMessageReader<String>(new StringDecoder()));
when(mockWebClientStrategies.messageReaders()).thenReturn(messageReaders::stream);
Flux<String> resultFlux = defaultClientResponse.bodyToFlux(String.class);
StepVerifier.create(resultFlux)
.expectError(WebClientException.class)
.verify();
}
}

View File

@@ -1,100 +0,0 @@
/*
* Copyright 2002-2016 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.client.reactive;
import org.junit.Test;
import reactor.core.publisher.Mono;
import org.springframework.http.HttpHeaders;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
/**
* @author Arjen Poutsma
*/
public class ExchangeFilterFunctionsTests {
@Test
public void andThen() throws Exception {
ClientRequest<Void> request = ClientRequest.GET("http://example.com").build();
ClientResponse response = mock(ClientResponse.class);
ExchangeFunction exchange = r -> Mono.just(response);
boolean[] filtersInvoked = new boolean[2];
ExchangeFilterFunction filter1 = (r, n) -> {
assertFalse(filtersInvoked[0]);
assertFalse(filtersInvoked[1]);
filtersInvoked[0] = true;
assertFalse(filtersInvoked[1]);
return n.exchange(r);
};
ExchangeFilterFunction filter2 = (r, n) -> {
assertTrue(filtersInvoked[0]);
assertFalse(filtersInvoked[1]);
filtersInvoked[1] = true;
return n.exchange(r);
};
ExchangeFilterFunction filter = filter1.andThen(filter2);
ClientResponse result = filter.filter(request, exchange).block();
assertEquals(response, result);
assertTrue(filtersInvoked[0]);
assertTrue(filtersInvoked[1]);
}
@Test
public void apply() throws Exception {
ClientRequest<Void> request = ClientRequest.GET("http://example.com").build();
ClientResponse response = mock(ClientResponse.class);
ExchangeFunction exchange = r -> Mono.just(response);
boolean[] filterInvoked = new boolean[1];
ExchangeFilterFunction filter = (r, n) -> {
assertFalse(filterInvoked[0]);
filterInvoked[0] = true;
return n.exchange(r);
};
ExchangeFunction filteredExchange = filter.apply(exchange);
ClientResponse result = filteredExchange.exchange(request).block();
assertEquals(response, result);
assertTrue(filterInvoked[0]);
}
@Test
public void basicAuthentication() throws Exception {
ClientRequest<Void> request = ClientRequest.GET("http://example.com").build();
ClientResponse response = mock(ClientResponse.class);
ExchangeFunction exchange = r -> {
assertTrue(r.headers().containsKey(HttpHeaders.AUTHORIZATION));
assertTrue(r.headers().getFirst(HttpHeaders.AUTHORIZATION).startsWith("Basic "));
return Mono.just(response);
};
ExchangeFilterFunction auth = ExchangeFilterFunctions.basicAuthentication("foo", "bar");
assertFalse(request.headers().containsKey(HttpHeaders.AUTHORIZATION));
ClientResponse result = auth.filter(request, exchange).block();
assertEquals(response, result);
}
}

View File

@@ -1,332 +0,0 @@
/*
* Copyright 2002-2016 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.client.reactive;
import java.time.Duration;
import okhttp3.HttpUrl;
import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.MockWebServer;
import okhttp3.mockwebserver.RecordedRequest;
import org.hamcrest.Matchers;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.http.codec.BodyExtractors;
import org.springframework.http.codec.BodyInserters;
import org.springframework.http.codec.Pojo;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.springframework.http.codec.BodyExtractors.toFlux;
import static org.springframework.http.codec.BodyExtractors.toMono;
/**
* {@link WebClient} integration tests with the {@code Flux} and {@code Mono} API.
*
* @author Brian Clozel
*/
public class WebClientIntegrationTests {
private MockWebServer server;
private WebClient webClient;
@Before
public void setup() {
this.server = new MockWebServer();
this.webClient = WebClient.create(new ReactorClientHttpConnector());
}
@Test
public void headers() throws Exception {
HttpUrl baseUrl = server.url("/greeting?name=Spring");
this.server.enqueue(new MockResponse().setHeader("Content-Type", "text/plain").setBody("Hello Spring!"));
ClientRequest<Void> request = ClientRequest.GET(baseUrl.toString()).build();
Mono<HttpHeaders> result = this.webClient
.exchange(request)
.map(response -> response.headers().asHttpHeaders());
StepVerifier.create(result)
.consumeNextWith(
httpHeaders -> {
assertEquals(MediaType.TEXT_PLAIN, httpHeaders.getContentType());
assertEquals(13L, httpHeaders.getContentLength());
})
.expectComplete()
.verify(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 plainText() throws Exception {
HttpUrl baseUrl = server.url("/greeting?name=Spring");
this.server.enqueue(new MockResponse().setBody("Hello Spring!"));
ClientRequest<Void> request = ClientRequest.GET(baseUrl.toString())
.header("X-Test-Header", "testvalue")
.build();
Mono<String> result = this.webClient
.exchange(request)
.then(response -> response.body(toMono(String.class)));
StepVerifier.create(result)
.expectNext("Hello Spring!")
.expectComplete()
.verify(Duration.ofSeconds(3));
RecordedRequest recordedRequest = server.takeRequest();
assertEquals(1, server.getRequestCount());
assertEquals("testvalue", recordedRequest.getHeader("X-Test-Header"));
assertEquals("*/*", recordedRequest.getHeader(HttpHeaders.ACCEPT));
assertEquals("/greeting?name=Spring", recordedRequest.getPath());
}
@Test
public void jsonString() throws Exception {
HttpUrl baseUrl = server.url("/json");
String content = "{\"bar\":\"barbar\",\"foo\":\"foofoo\"}";
this.server.enqueue(new MockResponse().setHeader("Content-Type", "application/json")
.setBody(content));
ClientRequest<Void> request = ClientRequest.GET(baseUrl.toString())
.accept(MediaType.APPLICATION_JSON)
.build();
Mono<String> result = this.webClient
.exchange(request)
.then(response -> response.body(toMono(String.class)));
StepVerifier.create(result)
.expectNext(content)
.expectComplete()
.verify(Duration.ofSeconds(3));
RecordedRequest recordedRequest = server.takeRequest();
assertEquals(1, server.getRequestCount());
assertEquals("/json", recordedRequest.getPath());
assertEquals("application/json", recordedRequest.getHeader(HttpHeaders.ACCEPT));
}
@Test
public void jsonPojoMono() throws Exception {
HttpUrl baseUrl = server.url("/pojo");
this.server.enqueue(new MockResponse().setHeader("Content-Type", "application/json")
.setBody("{\"bar\":\"barbar\",\"foo\":\"foofoo\"}"));
ClientRequest<Void> request = ClientRequest.GET(baseUrl.toString())
.accept(MediaType.APPLICATION_JSON)
.build();
Mono<Pojo> result = this.webClient
.exchange(request)
.then(response -> response.body(toMono(Pojo.class)));
StepVerifier.create(result)
.consumeNextWith(p -> assertEquals("barbar", p.getBar()))
.expectComplete()
.verify(Duration.ofSeconds(3));
RecordedRequest recordedRequest = server.takeRequest();
assertEquals(1, server.getRequestCount());
assertEquals("/pojo", recordedRequest.getPath());
assertEquals("application/json", recordedRequest.getHeader(HttpHeaders.ACCEPT));
}
@Test
public void jsonPojoFlux() throws Exception {
HttpUrl baseUrl = server.url("/pojos");
this.server.enqueue(new MockResponse().setHeader("Content-Type", "application/json")
.setBody("[{\"bar\":\"bar1\",\"foo\":\"foo1\"},{\"bar\":\"bar2\",\"foo\":\"foo2\"}]"));
ClientRequest<Void> request = ClientRequest.GET(baseUrl.toString())
.accept(MediaType.APPLICATION_JSON)
.build();
Flux<Pojo> result = this.webClient
.exchange(request)
.flatMap(response -> response.body(toFlux(Pojo.class)));
StepVerifier.create(result)
.consumeNextWith(p -> assertThat(p.getBar(), Matchers.is("bar1")))
.consumeNextWith(p -> assertThat(p.getBar(), Matchers.is("bar2")))
.expectComplete()
.verify(Duration.ofSeconds(3));
RecordedRequest recordedRequest = server.takeRequest();
assertEquals(1, server.getRequestCount());
assertEquals("/pojos", recordedRequest.getPath());
assertEquals("application/json", recordedRequest.getHeader(HttpHeaders.ACCEPT));
}
@Test
public void postJsonPojo() throws Exception {
HttpUrl baseUrl = server.url("/pojo/capitalize");
this.server.enqueue(new MockResponse()
.setHeader("Content-Type", "application/json")
.setBody("{\"bar\":\"BARBAR\",\"foo\":\"FOOFOO\"}"));
Pojo spring = new Pojo("foofoo", "barbar");
ClientRequest<Pojo> request = ClientRequest.POST(baseUrl.toString())
.accept(MediaType.APPLICATION_JSON)
.contentType(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromObject(spring));
Mono<Pojo> result = this.webClient
.exchange(request)
.then(response -> response.body(BodyExtractors.toMono(Pojo.class)));
StepVerifier.create(result)
.consumeNextWith(p -> assertEquals("BARBAR", p.getBar()))
.expectComplete()
.verify(Duration.ofSeconds(3));
RecordedRequest recordedRequest = server.takeRequest();
assertEquals(1, server.getRequestCount());
assertEquals("/pojo/capitalize", recordedRequest.getPath());
assertEquals("{\"foo\":\"foofoo\",\"bar\":\"barbar\"}", recordedRequest.getBody().readUtf8());
assertEquals("chunked", recordedRequest.getHeader(HttpHeaders.TRANSFER_ENCODING));
assertEquals("application/json", recordedRequest.getHeader(HttpHeaders.ACCEPT));
assertEquals("application/json", recordedRequest.getHeader(HttpHeaders.CONTENT_TYPE));
}
@Test
public void cookies() throws Exception {
HttpUrl baseUrl = server.url("/test");
this.server.enqueue(new MockResponse()
.setHeader("Content-Type", "text/plain").setBody("test"));
ClientRequest<Void> request = ClientRequest.GET(baseUrl.toString())
.cookie("testkey", "testvalue")
.build();
Mono<String> result = this.webClient
.exchange(request)
.then(response -> response.body(toMono(String.class)));
StepVerifier.create(result)
.expectNext("test")
.expectComplete()
.verify(Duration.ofSeconds(3));
RecordedRequest recordedRequest = server.takeRequest();
assertEquals(1, server.getRequestCount());
assertEquals("/test", recordedRequest.getPath());
assertEquals("testkey=testvalue", recordedRequest.getHeader(HttpHeaders.COOKIE));
}
@Test
public void notFound() 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<ClientResponse> result = this.webClient
.exchange(request);
StepVerifier.create(result)
.consumeNextWith(response -> {
assertEquals(HttpStatus.NOT_FOUND, response.statusCode());
})
.expectComplete()
.verify(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 buildFilter() throws Exception {
HttpUrl baseUrl = server.url("/greeting?name=Spring");
this.server.enqueue(new MockResponse().setHeader("Content-Type", "text/plain").setBody("Hello Spring!"));
ExchangeFilterFunction filter = (request, next) -> {
ClientRequest<?> filteredRequest = ClientRequest.from(request)
.header("foo", "bar").build();
return next.exchange(filteredRequest);
};
WebClient filteredClient = WebClient.builder(new ReactorClientHttpConnector())
.filter(filter).build();
ClientRequest<Void> request = ClientRequest.GET(baseUrl.toString()).build();
Mono<String> result = filteredClient.exchange(request)
.then(response -> response.body(toMono(String.class)));
StepVerifier.create(result)
.expectNext("Hello Spring!")
.expectComplete()
.verify(Duration.ofSeconds(3));
RecordedRequest recordedRequest = server.takeRequest();
assertEquals(1, server.getRequestCount());
assertEquals("bar", recordedRequest.getHeader("foo"));
}
@Test
public void filter() throws Exception {
HttpUrl baseUrl = server.url("/greeting?name=Spring");
this.server.enqueue(new MockResponse().setHeader("Content-Type", "text/plain").setBody("Hello Spring!"));
ExchangeFilterFunction filter = (request, next) -> {
ClientRequest<?> filteredRequest = ClientRequest.from(request)
.header("foo", "bar").build();
return next.exchange(filteredRequest);
};
WebClient client = WebClient.create(new ReactorClientHttpConnector());
WebClient filteredClient = client.filter(filter);
ClientRequest<Void> request = ClientRequest.GET(baseUrl.toString()).build();
Mono<String> result = filteredClient.exchange(request)
.then(response -> response.body(toMono(String.class)));
StepVerifier.create(result)
.expectNext("Hello Spring!")
.expectComplete()
.verify(Duration.ofSeconds(3));
RecordedRequest recordedRequest = server.takeRequest();
assertEquals(1, server.getRequestCount());
assertEquals("bar", recordedRequest.getHeader("foo"));
}
@After
public void tearDown() throws Exception {
this.server.shutdown();
}
}

View File

@@ -1,132 +0,0 @@
/*
* Copyright 2002-2016 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.client.reactive;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.junit.Test;
import org.reactivestreams.Publisher;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import org.springframework.context.support.StaticApplicationContext;
import org.springframework.core.ResolvableType;
import org.springframework.http.MediaType;
import org.springframework.http.ReactiveHttpInputMessage;
import org.springframework.http.ReactiveHttpOutputMessage;
import org.springframework.http.codec.HttpMessageReader;
import org.springframework.http.codec.HttpMessageWriter;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
/**
* @author Arjen Poutsma
*/
public class WebClientStrategiesTests {
@Test
public void empty() {
WebClientStrategies strategies = WebClientStrategies.empty().build();
assertEquals(Optional.empty(), strategies.messageReaders().get().findFirst());
assertEquals(Optional.empty(), strategies.messageWriters().get().findFirst());
}
@Test
public void ofSuppliers() {
HttpMessageReader<?> messageReader = new DummyMessageReader();
HttpMessageWriter<?> messageWriter = new DummyMessageWriter();
WebClientStrategies strategies = WebClientStrategies.of(
() -> Stream.of(messageReader),
() -> Stream.of(messageWriter));
assertEquals(1L, strategies.messageReaders().get().collect(Collectors.counting()).longValue());
assertEquals(Optional.of(messageReader), strategies.messageReaders().get().findFirst());
assertEquals(1L, strategies.messageWriters().get().collect(Collectors.counting()).longValue());
assertEquals(Optional.of(messageWriter), strategies.messageWriters().get().findFirst());
}
@Test
public void toConfiguration() throws Exception {
StaticApplicationContext applicationContext = new StaticApplicationContext();
applicationContext.registerSingleton("messageWriter", DummyMessageWriter.class);
applicationContext.registerSingleton("messageReader", DummyMessageReader.class);
applicationContext.refresh();
WebClientStrategies strategies = WebClientStrategies.of(applicationContext);
assertTrue(strategies.messageReaders().get()
.allMatch(r -> r instanceof DummyMessageReader));
assertTrue(strategies.messageWriters().get()
.allMatch(r -> r instanceof DummyMessageWriter));
}
private static class DummyMessageWriter implements HttpMessageWriter<Object> {
@Override
public boolean canWrite(ResolvableType type, MediaType mediaType) {
return false;
}
@Override
public List<MediaType> getWritableMediaTypes() {
return Collections.emptyList();
}
@Override
public Mono<Void> write(Publisher<?> inputStream, ResolvableType type,
MediaType contentType,
ReactiveHttpOutputMessage outputMessage,
Map<String, Object> hints) {
return Mono.empty();
}
}
private static class DummyMessageReader implements HttpMessageReader<Object> {
@Override
public boolean canRead(ResolvableType type, MediaType mediaType) {
return false;
}
@Override
public List<MediaType> getReadableMediaTypes() {
return Collections.emptyList();
}
@Override
public Flux<Object> read(ResolvableType type, ReactiveHttpInputMessage inputMessage,
Map<String, Object> hints) {
return Flux.empty();
}
@Override
public Mono<Object> readMono(ResolvableType type, ReactiveHttpInputMessage inputMessage,
Map<String, Object> hints) {
return Mono.empty();
}
}
}