/** * Adapt the given response processor function to a filter function that * only operates on the {@code ClientResponse}. * @param processor the response processor * @return the resulting filter adapter */ static ExchangeFilterFunction ofResponseProcessor(Function<ClientResponse, Mono<ClientResponse>> processor) { Assert.notNull(processor, "ClientResponse Function must not be null"); return (request, next) -> next.exchange(request).flatMap(processor); }
private ClientRequest verifyAndGetRequest() { ClientRequest request = this.captor.getValue(); Mockito.verify(this.exchangeFunction).exchange(request); verifyNoMoreInteractions(this.exchangeFunction); return request; }
public static ExchangeFilterFunction toExchangeFilterFunction(InstanceExchangeFilterFunction delegate) { return (request, next) -> { Optional<?> instance = request.attribute(ATTRIBUTE_INSTANCE); if (instance.isPresent() && instance.get() instanceof Instance) { return delegate.exchange((Instance) instance.get(), request, next); } return next.exchange(request); }; }
/** * Variant of {@link #basicAuthentication(String, String)} that looks up * the {@link Credentials Credentials} in a * {@link #BASIC_AUTHENTICATION_CREDENTIALS_ATTRIBUTE request attribute}. * @return the filter to use * @see Credentials * @deprecated as of Spring 5.1 in favor of using * {@link HttpHeaders#setBasicAuth(String, String)} while building the request. */ @Deprecated public static ExchangeFilterFunction basicAuthentication() { return (request, next) -> { Object attr = request.attributes().get(BASIC_AUTHENTICATION_CREDENTIALS_ATTRIBUTE); if (attr instanceof Credentials) { Credentials cred = (Credentials) attr; return next.exchange(ClientRequest.from(request) .headers(headers -> headers.setBasicAuth(cred.username, cred.password)) .build()); } else { return next.exchange(request); } }; }
public static ExchangeFilterFunction setDefaultAcceptHeader() { return (request, next) -> { if (request.headers().getAccept().isEmpty()) { Boolean isRequestForLogfile = request.attribute(ATTRIBUTE_ENDPOINT) .map(Endpoint.LOGFILE::equals) .orElse(false); List<MediaType> acceptedHeaders = isRequestForLogfile ? DEFAULT_LOGFILE_ACCEPT_MEDIATYPES : DEFAULT_ACCEPT_MEDIATYPES; return next.exchange(ClientRequest.from(request) .headers(headers -> headers.setAccept(acceptedHeaders)) .build()); } return next.exchange(request); }; }
/** * Return a filter that applies HTTP Basic Authentication to the request * headers via {@link HttpHeaders#setBasicAuth(String, String)}. * @param user the user * @param password the password * @return the filter to add authentication headers with * @see HttpHeaders#setBasicAuth(String, String) * @see HttpHeaders#setBasicAuth(String, String, Charset) */ public static ExchangeFilterFunction basicAuthentication(String user, String password) { return (request, next) -> next.exchange(ClientRequest.from(request) .headers(headers -> headers.setBasicAuth(user, password)) .build()); }
@Before public void setup() { MockitoAnnotations.initMocks(this); this.exchangeFunction = mock(ExchangeFunction.class); when(this.exchangeFunction.exchange(this.captor.capture())).thenReturn(Mono.empty()); this.builder = WebClient.builder().baseUrl("/base").exchangeFunction(this.exchangeFunction); }
/** * Consume up to the specified number of bytes from the response body and * cancel if any more data arrives. * <p>Internally delegates to {@link DataBufferUtils#takeUntilByteCount}. * @param maxByteCount the limit as number of bytes * @return the filter to limit the response size with * @since 5.1 */ public static ExchangeFilterFunction limitResponseSize(long maxByteCount) { return (request, next) -> next.exchange(request).map(response -> { Flux<DataBuffer> body = response.body(BodyExtractors.toDataBuffers()); body = DataBufferUtils.takeUntilByteCount(body, maxByteCount); return ClientResponse.from(response).body(body).build(); }); }
@Override public Mono<ClientResponse> exchange() { ClientRequest request = (this.inserter != null ? initRequestBuilder().body(this.inserter).build() : initRequestBuilder().build()); return exchangeFunction.exchange(request).switchIfEmpty(NO_HTTP_CLIENT_RESPONSE_ERROR); }
@Test public void apply() { ClientRequest request = ClientRequest.create(HttpMethod.GET, DEFAULT_URL).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]); }
public static ExchangeFilterFunction retry(int defaultRetries, Map<String, Integer> retriesPerEndpoint) { return (request, next) -> { int retries = 0; if (!request.method().equals(HttpMethod.DELETE) && !request.method().equals(HttpMethod.PATCH) && !request.method().equals(HttpMethod.POST) && !request.method().equals(HttpMethod.PUT)) { retries = request.attribute(ATTRIBUTE_ENDPOINT).map(retriesPerEndpoint::get).orElse(defaultRetries); } return next.exchange(request).retry(retries); }; } }
public static ExchangeFilterFunction addHeaders(HttpHeadersProvider httpHeadersProvider) { return toExchangeFilterFunction((instance, request, next) -> { ClientRequest newRequest = ClientRequest.from(request) .headers(headers -> headers.addAll(httpHeadersProvider.getHeaders( instance))) .build(); return next.exchange(newRequest); }); }
@Test public void withStringAttribute() { Map<String, Object> actual = new HashMap<>(); ExchangeFilterFunction filter = (request, next) -> { actual.putAll(request.attributes()); return next.exchange(request); }; this.builder.filter(filter).build() .get().uri("/path") .attribute("foo", "bar") .exchange(); assertEquals("bar", actual.get("foo")); ClientRequest request = verifyAndGetRequest(); assertEquals("bar", request.attribute("foo").get()); }
@Test public void withNullAttribute() { Map<String, Object> actual = new HashMap<>(); ExchangeFilterFunction filter = (request, next) -> { actual.putAll(request.attributes()); return next.exchange(request); }; this.builder.filter(filter).build() .get().uri("/path") .attribute("foo", null) .exchange(); assertNull(actual.get("foo")); ClientRequest request = verifyAndGetRequest(); assertFalse(request.attribute("foo").isPresent()); }
@Test public void defaultRequest() { ThreadLocal<String> context = new NamedThreadLocal<>("foo"); Map<String, Object> actual = new HashMap<>(); ExchangeFilterFunction filter = (request, next) -> { actual.putAll(request.attributes()); return next.exchange(request); }; WebClient client = this.builder .defaultRequest(spec -> spec.attribute("foo", context.get())) .filter(filter) .build(); try { context.set("bar"); client.get().uri("/path").attribute("foo", "bar").exchange(); } finally { context.remove(); } assertEquals("bar", actual.get("foo")); }
@Test public void captureAndClaim() { ClientHttpRequest request = new MockClientHttpRequest(HttpMethod.GET, "/test"); ClientHttpResponse response = new MockClientHttpResponse(HttpStatus.OK); ClientHttpConnector connector = (method, uri, fn) -> fn.apply(request).then(Mono.just(response)); ClientRequest clientRequest = ClientRequest.create(HttpMethod.GET, URI.create("/test")) .header(WebTestClient.WEBTESTCLIENT_REQUEST_ID, "1").build(); WiretapConnector wiretapConnector = new WiretapConnector(connector); ExchangeFunction function = ExchangeFunctions.create(wiretapConnector); function.exchange(clientRequest).block(ofMillis(0)); WiretapConnector.Info actual = wiretapConnector.claimRequest("1"); ExchangeResult result = actual.createExchangeResult(Duration.ZERO, null); assertEquals(HttpMethod.GET, result.getMethod()); assertEquals("/test", result.getUrl().toString()); }
@Test public void andThen() { ClientRequest request = ClientRequest.create(HttpMethod.GET, DEFAULT_URL).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]); }
.filter((request, next) -> next.exchange(request)) .defaultHeader("foo", "bar") .defaultCookie("foo", "bar"); WebClient client2 = builder.filter((request, next) -> next.exchange(request)) .defaultHeader("baz", "qux") .defaultCookie("baz", "qux") .filter((request, next) -> next.exchange(request)) .defaultHeader("baz", "qux") .defaultCookie("baz", "qux")
@Test // SPR-16059 public void mutateDoesCopy() { WebTestClient.Builder builder = WebTestClient .bindToWebHandler(exchange -> exchange.getResponse().setComplete()) .configureClient(); builder.filter((request, next) -> next.exchange(request)); builder.defaultHeader("foo", "bar"); builder.defaultCookie("foo", "bar"); WebTestClient client1 = builder.build(); builder.filter((request, next) -> next.exchange(request)); builder.defaultHeader("baz", "qux"); builder.defaultCookie("baz", "qux"); WebTestClient client2 = builder.build(); WebTestClient.Builder mutatedBuilder = client1.mutate(); mutatedBuilder.filter((request, next) -> next.exchange(request)); mutatedBuilder.defaultHeader("baz", "qux"); mutatedBuilder.defaultCookie("baz", "qux"); WebTestClient clientFromMutatedBuilder = mutatedBuilder.build(); client1.mutate().filters(filters -> assertEquals(1, filters.size())); client1.mutate().defaultHeaders(headers -> assertEquals(1, headers.size())); client1.mutate().defaultCookies(cookies -> assertEquals(1, cookies.size())); client2.mutate().filters(filters -> assertEquals(2, filters.size())); client2.mutate().defaultHeaders(headers -> assertEquals(2, headers.size())); client2.mutate().defaultCookies(cookies -> assertEquals(2, cookies.size())); clientFromMutatedBuilder.mutate().filters(filters -> assertEquals(2, filters.size())); clientFromMutatedBuilder.mutate().defaultHeaders(headers -> assertEquals(2, headers.size())); clientFromMutatedBuilder.mutate().defaultCookies(cookies -> assertEquals(2, cookies.size())); }
@Test public void shouldApplyExchangeFilter() { prepareResponse(response -> response.setHeader("Content-Type", "text/plain") .setBody("Hello Spring!")); WebClient filteredClient = this.webClient.mutate() .filter((request, next) -> { ClientRequest filteredRequest = ClientRequest.from(request).header("foo", "bar").build(); return next.exchange(filteredRequest); }) .build(); Mono<String> result = filteredClient.get() .uri("/greeting?name=Spring") .retrieve() .bodyToMono(String.class); StepVerifier.create(result) .expectNext("Hello Spring!") .expectComplete() .verify(Duration.ofSeconds(3)); expectRequestCount(1); expectRequest(request -> assertEquals("bar", request.getHeader("foo"))); }