001/* 002 * Copyright 2002-2020 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.web.reactive.function.client; 018 019import java.net.URI; 020import java.nio.charset.Charset; 021import java.time.ZonedDateTime; 022import java.util.List; 023import java.util.Map; 024import java.util.function.Consumer; 025import java.util.function.Function; 026import java.util.function.IntPredicate; 027import java.util.function.Predicate; 028 029import org.reactivestreams.Publisher; 030import reactor.core.publisher.Flux; 031import reactor.core.publisher.Mono; 032 033import org.springframework.core.ParameterizedTypeReference; 034import org.springframework.core.ReactiveAdapterRegistry; 035import org.springframework.http.HttpHeaders; 036import org.springframework.http.HttpMethod; 037import org.springframework.http.HttpStatus; 038import org.springframework.http.MediaType; 039import org.springframework.http.ResponseEntity; 040import org.springframework.http.client.reactive.ClientHttpConnector; 041import org.springframework.http.client.reactive.ClientHttpRequest; 042import org.springframework.http.codec.ClientCodecConfigurer; 043import org.springframework.util.MultiValueMap; 044import org.springframework.web.reactive.function.BodyInserter; 045import org.springframework.web.reactive.function.BodyInserters; 046import org.springframework.web.util.DefaultUriBuilderFactory; 047import org.springframework.web.util.UriBuilder; 048import org.springframework.web.util.UriBuilderFactory; 049 050/** 051 * Non-blocking, reactive client to perform HTTP requests, exposing a fluent, 052 * reactive API over underlying HTTP client libraries such as Reactor Netty. 053 * 054 * <p>Use static factory methods {@link #create()} or {@link #create(String)}, 055 * or {@link WebClient#builder()} to prepare an instance. 056 * 057 * <p>For examples with a response body see: 058 * <ul> 059 * <li>{@link RequestHeadersSpec#retrieve() retrieve()} 060 * <li>{@link RequestHeadersSpec#exchange() exchange()} 061 * </ul> 062 * <p>For examples with a request body see: 063 * <ul> 064 * <li>{@link RequestBodySpec#bodyValue(Object) bodyValue(Object)} 065 * <li>{@link RequestBodySpec#body(Publisher, Class) body(Publisher,Class)} 066 * </ul> 067 * 068 * @author Rossen Stoyanchev 069 * @author Arjen Poutsma 070 * @author Sebastien Deleuze 071 * @author Brian Clozel 072 * @since 5.0 073 */ 074public interface WebClient { 075 076 /** 077 * Start building an HTTP GET request. 078 * @return a spec for specifying the target URL 079 */ 080 RequestHeadersUriSpec<?> get(); 081 082 /** 083 * Start building an HTTP HEAD request. 084 * @return a spec for specifying the target URL 085 */ 086 RequestHeadersUriSpec<?> head(); 087 088 /** 089 * Start building an HTTP POST request. 090 * @return a spec for specifying the target URL 091 */ 092 RequestBodyUriSpec post(); 093 094 /** 095 * Start building an HTTP PUT request. 096 * @return a spec for specifying the target URL 097 */ 098 RequestBodyUriSpec put(); 099 100 /** 101 * Start building an HTTP PATCH request. 102 * @return a spec for specifying the target URL 103 */ 104 RequestBodyUriSpec patch(); 105 106 /** 107 * Start building an HTTP DELETE request. 108 * @return a spec for specifying the target URL 109 */ 110 RequestHeadersUriSpec<?> delete(); 111 112 /** 113 * Start building an HTTP OPTIONS request. 114 * @return a spec for specifying the target URL 115 */ 116 RequestHeadersUriSpec<?> options(); 117 118 /** 119 * Start building a request for the given {@code HttpMethod}. 120 * @return a spec for specifying the target URL 121 */ 122 RequestBodyUriSpec method(HttpMethod method); 123 124 125 /** 126 * Return a builder to create a new {@code WebClient} whose settings are 127 * replicated from the current {@code WebClient}. 128 */ 129 Builder mutate(); 130 131 132 // Static, factory methods 133 134 /** 135 * Create a new {@code WebClient} with Reactor Netty by default. 136 * @see #create(String) 137 * @see #builder() 138 */ 139 static WebClient create() { 140 return new DefaultWebClientBuilder().build(); 141 } 142 143 /** 144 * Variant of {@link #create()} that accepts a default base URL. For more 145 * details see {@link Builder#baseUrl(String) Builder.baseUrl(String)}. 146 * @param baseUrl the base URI for all requests 147 * @see #builder() 148 */ 149 static WebClient create(String baseUrl) { 150 return new DefaultWebClientBuilder().baseUrl(baseUrl).build(); 151 } 152 153 /** 154 * Obtain a {@code WebClient} builder. 155 */ 156 static WebClient.Builder builder() { 157 return new DefaultWebClientBuilder(); 158 } 159 160 161 /** 162 * A mutable builder for creating a {@link WebClient}. 163 */ 164 interface Builder { 165 166 /** 167 * Configure a base URL for requests. Effectively a shortcut for: 168 * <p> 169 * <pre class="code"> 170 * String baseUrl = "https://abc.go.com/v1"; 171 * DefaultUriBuilderFactory factory = new DefaultUriBuilderFactory(baseUrl); 172 * WebClient client = WebClient.builder().uriBuilderFactory(factory).build(); 173 * </pre> 174 * <p>The {@code DefaultUriBuilderFactory} is used to prepare the URL 175 * for every request with the given base URL, unless the URL request 176 * for a given URL is absolute in which case the base URL is ignored. 177 * <p><strong>Note:</strong> this method is mutually exclusive with 178 * {@link #uriBuilderFactory(UriBuilderFactory)}. If both are used, the 179 * baseUrl value provided here will be ignored. 180 * @see DefaultUriBuilderFactory#DefaultUriBuilderFactory(String) 181 * @see #uriBuilderFactory(UriBuilderFactory) 182 */ 183 Builder baseUrl(String baseUrl); 184 185 /** 186 * Configure default URI variable values that will be used when expanding 187 * URI templates using a {@link Map}. 188 * @param defaultUriVariables the default values to use 189 * @see #baseUrl(String) 190 * @see #uriBuilderFactory(UriBuilderFactory) 191 */ 192 /** 193 * Configure default URL variable values to use when expanding URI 194 * templates with a {@link Map}. Effectively a shortcut for: 195 * <p> 196 * <pre class="code"> 197 * Map<String, ?> defaultVars = ...; 198 * DefaultUriBuilderFactory factory = new DefaultUriBuilderFactory(); 199 * factory.setDefaultVariables(defaultVars); 200 * WebClient client = WebClient.builder().uriBuilderFactory(factory).build(); 201 * </pre> 202 * <p><strong>Note:</strong> this method is mutually exclusive with 203 * {@link #uriBuilderFactory(UriBuilderFactory)}. If both are used, the 204 * defaultUriVariables value provided here will be ignored. 205 * @see DefaultUriBuilderFactory#setDefaultUriVariables(Map) 206 * @see #uriBuilderFactory(UriBuilderFactory) 207 */ 208 Builder defaultUriVariables(Map<String, ?> defaultUriVariables); 209 210 /** 211 * Provide a pre-configured {@link UriBuilderFactory} instance. This is 212 * an alternative to, and effectively overrides the following shortcut 213 * properties: 214 * <ul> 215 * <li>{@link #baseUrl(String)} 216 * <li>{@link #defaultUriVariables(Map)}. 217 * </ul> 218 * @param uriBuilderFactory the URI builder factory to use 219 * @see #baseUrl(String) 220 * @see #defaultUriVariables(Map) 221 */ 222 Builder uriBuilderFactory(UriBuilderFactory uriBuilderFactory); 223 224 /** 225 * Global option to specify a header to be added to every request, 226 * if the request does not already contain such a header. 227 * @param header the header name 228 * @param values the header values 229 */ 230 Builder defaultHeader(String header, String... values); 231 232 /** 233 * Provides access to every {@link #defaultHeader(String, String...)} 234 * declared so far with the possibility to add, replace, or remove. 235 * @param headersConsumer the consumer 236 */ 237 Builder defaultHeaders(Consumer<HttpHeaders> headersConsumer); 238 239 /** 240 * Global option to specify a cookie to be added to every request, 241 * if the request does not already contain such a cookie. 242 * @param cookie the cookie name 243 * @param values the cookie values 244 */ 245 Builder defaultCookie(String cookie, String... values); 246 247 /** 248 * Provides access to every {@link #defaultCookie(String, String...)} 249 * declared so far with the possibility to add, replace, or remove. 250 * @param cookiesConsumer a function that consumes the cookies map 251 */ 252 Builder defaultCookies(Consumer<MultiValueMap<String, String>> cookiesConsumer); 253 254 /** 255 * Provide a consumer to modify every request being built just before the 256 * call to {@link RequestHeadersSpec#exchange() exchange()}. 257 * @param defaultRequest the consumer to use for modifying requests 258 * @since 5.1 259 */ 260 Builder defaultRequest(Consumer<RequestHeadersSpec<?>> defaultRequest); 261 262 /** 263 * Add the given filter to the end of the filter chain. 264 * @param filter the filter to be added to the chain 265 */ 266 Builder filter(ExchangeFilterFunction filter); 267 268 /** 269 * Manipulate the filters with the given consumer. The list provided to 270 * the consumer is "live", so that the consumer can be used to remove 271 * filters, change ordering, etc. 272 * @param filtersConsumer a function that consumes the filter list 273 * @return this builder 274 */ 275 Builder filters(Consumer<List<ExchangeFilterFunction>> filtersConsumer); 276 277 /** 278 * Configure the {@link ClientHttpConnector} to use. This is useful for 279 * plugging in and/or customizing options of the underlying HTTP client 280 * library (e.g. SSL). 281 * <p>By default this is set to 282 * {@link org.springframework.http.client.reactive.ReactorClientHttpConnector 283 * ReactorClientHttpConnector}. 284 * @param connector the connector to use 285 */ 286 Builder clientConnector(ClientHttpConnector connector); 287 288 /** 289 * Configure the codecs for the {@code WebClient} in the 290 * {@link #exchangeStrategies(ExchangeStrategies) underlying} 291 * {@code ExchangeStrategies}. 292 * @param configurer the configurer to apply 293 * @since 5.1.13 294 */ 295 Builder codecs(Consumer<ClientCodecConfigurer> configurer); 296 297 /** 298 * Configure the {@link ExchangeStrategies} to use. 299 * <p>For most cases, prefer using {@link #codecs(Consumer)} which allows 300 * customizing the codecs in the {@code ExchangeStrategies} rather than 301 * replace them. That ensures multiple parties can contribute to codecs 302 * configuration. 303 * <p>By default this is set to {@link ExchangeStrategies#withDefaults()}. 304 * @param strategies the strategies to use 305 */ 306 Builder exchangeStrategies(ExchangeStrategies strategies); 307 308 /** 309 * Customize the strategies configured via 310 * {@link #exchangeStrategies(ExchangeStrategies)}. This method is 311 * designed for use in scenarios where multiple parties wish to update 312 * the {@code ExchangeStrategies}. 313 * @deprecated as of 5.1.13 in favor of {@link #codecs(Consumer)} 314 */ 315 @Deprecated 316 Builder exchangeStrategies(Consumer<ExchangeStrategies.Builder> configurer); 317 318 /** 319 * Provide an {@link ExchangeFunction} pre-configured with 320 * {@link ClientHttpConnector} and {@link ExchangeStrategies}. 321 * <p>This is an alternative to, and effectively overrides 322 * {@link #clientConnector}, and 323 * {@link #exchangeStrategies(ExchangeStrategies)}. 324 * @param exchangeFunction the exchange function to use 325 */ 326 Builder exchangeFunction(ExchangeFunction exchangeFunction); 327 328 /** 329 * Apply the given {@code Consumer} to this builder instance. 330 * <p>This can be useful for applying pre-packaged customizations. 331 * @param builderConsumer the consumer to apply 332 */ 333 Builder apply(Consumer<Builder> builderConsumer); 334 335 /** 336 * Clone this {@code WebClient.Builder}. 337 */ 338 Builder clone(); 339 340 /** 341 * Builder the {@link WebClient} instance. 342 */ 343 WebClient build(); 344 } 345 346 347 /** 348 * Contract for specifying the URI for a request. 349 * @param <S> a self reference to the spec type 350 */ 351 interface UriSpec<S extends RequestHeadersSpec<?>> { 352 353 /** 354 * Specify the URI using an absolute, fully constructed {@link URI}. 355 */ 356 S uri(URI uri); 357 358 /** 359 * Specify the URI for the request using a URI template and URI variables. 360 * If a {@link UriBuilderFactory} was configured for the client (e.g. 361 * with a base URI) it will be used to expand the URI template. 362 */ 363 S uri(String uri, Object... uriVariables); 364 365 /** 366 * Specify the URI for the request using a URI template and URI variables. 367 * If a {@link UriBuilderFactory} was configured for the client (e.g. 368 * with a base URI) it will be used to expand the URI template. 369 */ 370 S uri(String uri, Map<String, ?> uriVariables); 371 372 /** 373 * Specify the URI starting with a URI template and finishing off with a 374 * {@link UriBuilder} created from the template. 375 * @since 5.2 376 */ 377 S uri(String uri, Function<UriBuilder, URI> uriFunction); 378 379 /** 380 * Specify the URI by through a {@link UriBuilder}. 381 * @see #uri(String, Function) 382 */ 383 S uri(Function<UriBuilder, URI> uriFunction); 384 } 385 386 387 /** 388 * Contract for specifying request headers leading up to the exchange. 389 * @param <S> a self reference to the spec type 390 */ 391 interface RequestHeadersSpec<S extends RequestHeadersSpec<S>> { 392 393 /** 394 * Set the list of acceptable {@linkplain MediaType media types}, as 395 * specified by the {@code Accept} header. 396 * @param acceptableMediaTypes the acceptable media types 397 * @return this builder 398 */ 399 S accept(MediaType... acceptableMediaTypes); 400 401 /** 402 * Set the list of acceptable {@linkplain Charset charsets}, as specified 403 * by the {@code Accept-Charset} header. 404 * @param acceptableCharsets the acceptable charsets 405 * @return this builder 406 */ 407 S acceptCharset(Charset... acceptableCharsets); 408 409 /** 410 * Add a cookie with the given name and value. 411 * @param name the cookie name 412 * @param value the cookie value 413 * @return this builder 414 */ 415 S cookie(String name, String value); 416 417 /** 418 * Provides access to every cookie declared so far with the possibility 419 * to add, replace, or remove values. 420 * @param cookiesConsumer the consumer to provide access to 421 * @return this builder 422 */ 423 S cookies(Consumer<MultiValueMap<String, String>> cookiesConsumer); 424 425 /** 426 * Set the value of the {@code If-Modified-Since} header. 427 * <p>The date should be specified as the number of milliseconds since 428 * January 1, 1970 GMT. 429 * @param ifModifiedSince the new value of the header 430 * @return this builder 431 */ 432 S ifModifiedSince(ZonedDateTime ifModifiedSince); 433 434 /** 435 * Set the values of the {@code If-None-Match} header. 436 * @param ifNoneMatches the new value of the header 437 * @return this builder 438 */ 439 S ifNoneMatch(String... ifNoneMatches); 440 441 /** 442 * Add the given, single header value under the given name. 443 * @param headerName the header name 444 * @param headerValues the header value(s) 445 * @return this builder 446 */ 447 S header(String headerName, String... headerValues); 448 449 /** 450 * Provides access to every header declared so far with the possibility 451 * to add, replace, or remove values. 452 * @param headersConsumer the consumer to provide access to 453 * @return this builder 454 */ 455 S headers(Consumer<HttpHeaders> headersConsumer); 456 457 /** 458 * Set the attribute with the given name to the given value. 459 * @param name the name of the attribute to add 460 * @param value the value of the attribute to add 461 * @return this builder 462 */ 463 S attribute(String name, Object value); 464 465 /** 466 * Provides access to every attribute declared so far with the 467 * possibility to add, replace, or remove values. 468 * @param attributesConsumer the consumer to provide access to 469 * @return this builder 470 */ 471 S attributes(Consumer<Map<String, Object>> attributesConsumer); 472 473 /** 474 * Perform the HTTP request and retrieve the response body: 475 * <p><pre> 476 * Mono<Person> bodyMono = client.get() 477 * .uri("/persons/1") 478 * .accept(MediaType.APPLICATION_JSON) 479 * .retrieve() 480 * .bodyToMono(Person.class); 481 * </pre> 482 * <p>This method is a shortcut to using {@link #exchange()} and 483 * decoding the response body through {@link ClientResponse}. 484 * @return {@code ResponseSpec} to specify how to decode the body 485 * @see #exchange() 486 */ 487 ResponseSpec retrieve(); 488 489 /** 490 * Perform the HTTP request and return a {@link ClientResponse} with the 491 * response status and headers. You can then use methods of the response 492 * to consume the body: 493 * <p><pre> 494 * Mono<Person> mono = client.get() 495 * .uri("/persons/1") 496 * .accept(MediaType.APPLICATION_JSON) 497 * .exchange() 498 * .flatMap(response -> response.bodyToMono(Person.class)); 499 * 500 * Flux<Person> flux = client.get() 501 * .uri("/persons") 502 * .accept(MediaType.APPLICATION_STREAM_JSON) 503 * .exchange() 504 * .flatMapMany(response -> response.bodyToFlux(Person.class)); 505 * </pre> 506 * <p><strong>NOTE:</strong> Unlike {@link #retrieve()}, when using 507 * {@code exchange()}, it is the responsibility of the application to 508 * consume any response content regardless of the scenario (success, 509 * error, unexpected data, etc). Not doing so can cause a memory leak. 510 * See {@link ClientResponse} for a list of all the available options 511 * for consuming the body. Generally prefer using {@link #retrieve()} 512 * unless you have a good reason to use {@code exchange()} which does 513 * allow to check the response status and headers before deciding how or 514 * if to consume the response. 515 * @return a {@code Mono} for the response 516 * @see #retrieve() 517 */ 518 Mono<ClientResponse> exchange(); 519 } 520 521 522 /** 523 * Contract for specifying request headers and body leading up to the exchange. 524 */ 525 interface RequestBodySpec extends RequestHeadersSpec<RequestBodySpec> { 526 527 /** 528 * Set the length of the body in bytes, as specified by the 529 * {@code Content-Length} header. 530 * @param contentLength the content length 531 * @return this builder 532 * @see HttpHeaders#setContentLength(long) 533 */ 534 RequestBodySpec contentLength(long contentLength); 535 536 /** 537 * Set the {@linkplain MediaType media type} of the body, as specified 538 * by the {@code Content-Type} header. 539 * @param contentType the content type 540 * @return this builder 541 * @see HttpHeaders#setContentType(MediaType) 542 */ 543 RequestBodySpec contentType(MediaType contentType); 544 545 /** 546 * Shortcut for {@link #body(BodyInserter)} with a 547 * {@linkplain BodyInserters#fromValue value inserter}. 548 * For example: 549 * <p><pre class="code"> 550 * Person person = ... ; 551 * 552 * Mono<Void> result = client.post() 553 * .uri("/persons/{id}", id) 554 * .contentType(MediaType.APPLICATION_JSON) 555 * .bodyValue(person) 556 * .retrieve() 557 * .bodyToMono(Void.class); 558 * </pre> 559 * <p>For multipart requests consider providing 560 * {@link org.springframework.util.MultiValueMap MultiValueMap} prepared 561 * with {@link org.springframework.http.client.MultipartBodyBuilder 562 * MultipartBodyBuilder}. 563 * @param body the value to write to the request body 564 * @return this builder 565 * @throws IllegalArgumentException if {@code body} is a 566 * {@link Publisher} or producer known to {@link ReactiveAdapterRegistry} 567 * @since 5.2 568 */ 569 RequestHeadersSpec<?> bodyValue(Object body); 570 571 /** 572 * Shortcut for {@link #body(BodyInserter)} with a 573 * {@linkplain BodyInserters#fromPublisher Publisher inserter}. 574 * For example: 575 * <p><pre> 576 * Mono<Person> personMono = ... ; 577 * 578 * Mono<Void> result = client.post() 579 * .uri("/persons/{id}", id) 580 * .contentType(MediaType.APPLICATION_JSON) 581 * .body(personMono, Person.class) 582 * .retrieve() 583 * .bodyToMono(Void.class); 584 * </pre> 585 * @param publisher the {@code Publisher} to write to the request 586 * @param elementClass the type of elements published 587 * @param <T> the type of the elements contained in the publisher 588 * @param <P> the type of the {@code Publisher} 589 * @return this builder 590 */ 591 <T, P extends Publisher<T>> RequestHeadersSpec<?> body(P publisher, Class<T> elementClass); 592 593 /** 594 * Variant of {@link #body(Publisher, Class)} that allows providing 595 * element type information with generics. 596 * @param publisher the {@code Publisher} to write to the request 597 * @param elementTypeRef the type of elements published 598 * @param <T> the type of the elements contained in the publisher 599 * @param <P> the type of the {@code Publisher} 600 * @return this builder 601 */ 602 <T, P extends Publisher<T>> RequestHeadersSpec<?> body(P publisher, 603 ParameterizedTypeReference<T> elementTypeRef); 604 605 /** 606 * Variant of {@link #body(Publisher, Class)} that allows using any 607 * producer that can be resolved to {@link Publisher} via 608 * {@link ReactiveAdapterRegistry}. 609 * @param producer the producer to write to the request 610 * @param elementClass the type of elements produced 611 * @return this builder 612 * @since 5.2 613 */ 614 RequestHeadersSpec<?> body(Object producer, Class<?> elementClass); 615 616 /** 617 * Variant of {@link #body(Publisher, ParameterizedTypeReference)} that 618 * allows using any producer that can be resolved to {@link Publisher} 619 * via {@link ReactiveAdapterRegistry}. 620 * @param producer the producer to write to the request 621 * @param elementTypeRef the type of elements produced 622 * @return this builder 623 * @since 5.2 624 */ 625 RequestHeadersSpec<?> body(Object producer, ParameterizedTypeReference<?> elementTypeRef); 626 627 /** 628 * Set the body of the request using the given body inserter. 629 * See {@link BodyInserters} for built-in {@link BodyInserter} implementations. 630 * @param inserter the body inserter to use for the request body 631 * @return this builder 632 * @see org.springframework.web.reactive.function.BodyInserters 633 */ 634 RequestHeadersSpec<?> body(BodyInserter<?, ? super ClientHttpRequest> inserter); 635 636 /** 637 * Shortcut for {@link #body(BodyInserter)} with a 638 * {@linkplain BodyInserters#fromValue value inserter}. 639 * As of 5.2 this method delegates to {@link #bodyValue(Object)}. 640 * @deprecated as of Spring Framework 5.2 in favor of {@link #bodyValue(Object)} 641 */ 642 @Deprecated 643 RequestHeadersSpec<?> syncBody(Object body); 644 } 645 646 647 /** 648 * Contract for specifying response operations following the exchange. 649 */ 650 interface ResponseSpec { 651 652 /** 653 * Provide a function to map specific error status codes to an error 654 * signal to be propagated downstream instead of the response. 655 * <p>By default, if there are no matching status handlers, responses 656 * with status codes >= 400 are mapped to 657 * {@link WebClientResponseException} which is created with 658 * {@link ClientResponse#createException()}. 659 * <p>To suppress the treatment of a status code as an error and process 660 * it as a normal response, return {@code Mono.empty()} from the function. 661 * The response will then propagate downstream to be processed. 662 * <p>To ignore an error response completely, and propagate neither 663 * response nor error, use a {@link ExchangeFilterFunction filter}, or 664 * add {@code onErrorResume} downstream, for example: 665 * <pre class="code"> 666 * webClient.get() 667 * .uri("https://abc.com/account/123") 668 * .retrieve() 669 * .bodyToMono(Account.class) 670 * .onErrorResume(WebClientResponseException.class, 671 * ex -> ex.getRawStatusCode() == 404 ? Mono.empty() : Mono.error(ex)); 672 * </pre> 673 * @param statusPredicate to match responses with 674 * @param exceptionFunction to map the response to an error signal 675 * @return this builder 676 * @see ClientResponse#createException() 677 */ 678 ResponseSpec onStatus(Predicate<HttpStatus> statusPredicate, 679 Function<ClientResponse, Mono<? extends Throwable>> exceptionFunction); 680 681 /** 682 * Variant of {@link #onStatus(Predicate, Function)} that works with 683 * raw status code values. This is useful for custom status codes. 684 * @param statusCodePredicate to match responses with 685 * @param exceptionFunction to map the response to an error signal 686 * @return this builder 687 * @since 5.1.9 688 */ 689 ResponseSpec onRawStatus(IntPredicate statusCodePredicate, 690 Function<ClientResponse, Mono<? extends Throwable>> exceptionFunction); 691 692 /** 693 * Extract the body to a {@code Mono}. By default, if the response has status code 4xx or 694 * 5xx, the {@code Mono} will contain a {@link WebClientException}. This can be overridden 695 * with {@link #onStatus(Predicate, Function)}. 696 * @param elementClass the expected response body element class 697 * @param <T> response body type 698 * @return a mono containing the body, or a {@link WebClientResponseException} if the 699 * status code is 4xx or 5xx 700 */ 701 <T> Mono<T> bodyToMono(Class<T> elementClass); 702 703 /** 704 * Extract the body to a {@code Mono}. By default, if the response has status code 4xx or 705 * 5xx, the {@code Mono} will contain a {@link WebClientException}. This can be overridden 706 * with {@link #onStatus(Predicate, Function)}. 707 * @param elementTypeRef a type reference describing the expected response body element type 708 * @param <T> response body type 709 * @return a mono containing the body, or a {@link WebClientResponseException} if the 710 * status code is 4xx or 5xx 711 */ 712 <T> Mono<T> bodyToMono(ParameterizedTypeReference<T> elementTypeRef); 713 714 /** 715 * Extract the body to a {@code Flux}. By default, if the response has status code 4xx or 716 * 5xx, the {@code Flux} will contain a {@link WebClientException}. This can be overridden 717 * with {@link #onStatus(Predicate, Function)}. 718 * @param elementClass the class of elements in the response 719 * @param <T> the type of elements in the response 720 * @return a flux containing the body, or a {@link WebClientResponseException} if the 721 * status code is 4xx or 5xx 722 */ 723 <T> Flux<T> bodyToFlux(Class<T> elementClass); 724 725 /** 726 * Extract the body to a {@code Flux}. By default, if the response has status code 4xx or 727 * 5xx, the {@code Flux} will contain a {@link WebClientException}. This can be overridden 728 * with {@link #onStatus(Predicate, Function)}. 729 * @param elementTypeRef a type reference describing the expected response body element type 730 * @param <T> the type of elements in the response 731 * @return a flux containing the body, or a {@link WebClientResponseException} if the 732 * status code is 4xx or 5xx 733 */ 734 <T> Flux<T> bodyToFlux(ParameterizedTypeReference<T> elementTypeRef); 735 736 /** 737 * Return the response as a delayed {@code ResponseEntity}. By default, if the response has 738 * status code 4xx or 5xx, the {@code Mono} will contain a {@link WebClientException}. This 739 * can be overridden with {@link #onStatus(Predicate, Function)}. 740 * @param bodyClass the expected response body type 741 * @param <T> response body type 742 * @return {@code Mono} with the {@code ResponseEntity} 743 * @since 5.2 744 */ 745 <T> Mono<ResponseEntity<T>> toEntity(Class<T> bodyClass); 746 747 /** 748 * Return the response as a delayed {@code ResponseEntity}. By default, if the response has 749 * status code 4xx or 5xx, the {@code Mono} will contain a {@link WebClientException}. This 750 * can be overridden with {@link #onStatus(Predicate, Function)}. 751 * @param bodyTypeReference a type reference describing the expected response body type 752 * @param <T> response body type 753 * @return {@code Mono} with the {@code ResponseEntity} 754 * @since 5.2 755 */ 756 <T> Mono<ResponseEntity<T>> toEntity(ParameterizedTypeReference<T> bodyTypeReference); 757 758 /** 759 * Return the response as a delayed list of {@code ResponseEntity}s. By default, if the 760 * response has status code 4xx or 5xx, the {@code Mono} will contain a 761 * {@link WebClientException}. This can be overridden with 762 * {@link #onStatus(Predicate, Function)}. 763 * @param elementClass the expected response body list element class 764 * @param <T> the type of elements in the list 765 * @return {@code Mono} with the list of {@code ResponseEntity}s 766 * @since 5.2 767 */ 768 <T> Mono<ResponseEntity<List<T>>> toEntityList(Class<T> elementClass); 769 770 /** 771 * Return the response as a delayed list of {@code ResponseEntity}s. By default, if the 772 * response has status code 4xx or 5xx, the {@code Mono} will contain a 773 * {@link WebClientException}. This can be overridden with 774 * {@link #onStatus(Predicate, Function)}. 775 * @param elementTypeRef the expected response body list element reference type 776 * @param <T> the type of elements in the list 777 * @return {@code Mono} with the list of {@code ResponseEntity}s 778 * @since 5.2 779 */ 780 <T> Mono<ResponseEntity<List<T>>> toEntityList(ParameterizedTypeReference<T> elementTypeRef); 781 782 /** 783 * Return the response as a delayed {@code ResponseEntity} containing status and headers, 784 * but no body. By default, if the response has status code 4xx or 5xx, the {@code Mono} 785 * will contain a {@link WebClientException}. This can be overridden with 786 * {@link #onStatus(Predicate, Function)}. 787 * Calling this method will {@linkplain ClientResponse#releaseBody() release} the body of 788 * the response. 789 * @return {@code Mono} with the bodiless {@code ResponseEntity} 790 * @since 5.2 791 */ 792 Mono<ResponseEntity<Void>> toBodilessEntity(); 793 } 794 795 796 /** 797 * Contract for specifying request headers and URI for a request. 798 * @param <S> a self reference to the spec type 799 */ 800 interface RequestHeadersUriSpec<S extends RequestHeadersSpec<S>> 801 extends UriSpec<S>, RequestHeadersSpec<S> { 802 } 803 804 805 /** 806 * Contract for specifying request headers, body and URI for a request. 807 */ 808 interface RequestBodyUriSpec extends RequestBodySpec, RequestHeadersUriSpec<RequestBodySpec> { 809 } 810 811 812}