Skip to content

Latest commit

 

History

History
262 lines (194 loc) · 7.1 KB

File metadata and controls

262 lines (194 loc) · 7.1 KB

WebClient

The spring-webflux module includes a non-blocking, reactive client for HTTP requests with Reactive Streams back pressure. It shares HTTP codecs and other infrastructure with the server functional web framework.

WebClient provides a higher level API over HTTP client libraries. By default it uses Reactor Netty but that is pluggable with a different ClientHttpConnector. The WebClient API returns Reactor Flux or Mono for output and accepts Reactive Streams Publisher as input (see web-reactive.adoc).

Tip

By comparison to the RestTemplate, the WebClient offers a more functional and fluent API that taking full advantage of Java 8 lambdas. It supports both sync and async scenarios, including streaming, and brings the efficiency of non-blocking I/O.

Retrieve

The retrieve() method is the easiest way to get a response body and decode it:

    WebClient client = WebClient.create("http://example.org");

    Mono<Person> result = client.get()
            .uri("/persons/{id}", id).accept(MediaType.APPLICATION_JSON)
            .retrieve()
            .bodyToMono(Person.class);

You can also get a stream of objects decoded from the response:

    Flux<Quote> result = client.get()
            .uri("/quotes").accept(MediaType.TEXT_EVENT_STREAM)
            .retrieve()
            .bodyToFlux(Quote.class);

By default, responses with 4xx or 5xx status codes result in an error of type WebClientResponseException but you can customize that:

    Mono<Person> result = client.get()
            .uri("/persons/{id}", id).accept(MediaType.APPLICATION_JSON)
            .retrieve()
            .onStatus(HttpStatus::is4xxServerError, response -> ...)
            .onStatus(HttpStatus::is5xxServerError, response -> ...)
            .bodyToMono(Person.class);

Exchange

The exchange() method provides more control. The below example is equivalent to retrieve() but also provides access to the ClientResponse:

    Mono<Person> result = client.get()
            .uri("/persons/{id}", id).accept(MediaType.APPLICATION_JSON)
            .exchange()
            .flatMap(response -> response.bodyToMono(Person.class));

At this level you can also create a full ResponseEntity:

    Mono<ResponseEntity<Person>> result = client.get()
            .uri("/persons/{id}", id).accept(MediaType.APPLICATION_JSON)
            .exchange()
            .flatMap(response -> response.toEntity(Person.class));

Note that unlike retrieve(), with exchange() there are no automatic error signals for 4xx and 5xx responses. You have to check the status code and decide how to proceed.

Caution

When using exchange() you must always use any of the body or entity methods of ClientResponse to ensure resources are released and to avoid potential issues with HTTP connection pooling. If not interested in the response body use bodyToMono(Void.class) to complete.

Request body

The request body can be encoded from an Object:

    Mono<Person> personMono = ... ;

    Mono<Void> result = client.post()
            .uri("/persons/{id}", id)
            .contentType(MediaType.APPLICATION_JSON)
            .body(personMono, Person.class)
            .retrieve()
            .bodyToMono(Void.class);

You can also have a stream of objects encoded:

    Flux<Person> personFlux = ... ;

    Mono<Void> result = client.post()
            .uri("/persons/{id}", id)
            .contentType(MediaType.APPLICATION_STREAM_JSON)
            .body(personFlux, Person.class)
            .retrieve()
            .bodyToMono(Void.class);

Or if you have the actual value, use the syncBody shortcut method:

    Person person = ... ;

    Mono<Void> result = client.post()
            .uri("/persons/{id}", id)
            .contentType(MediaType.APPLICATION_JSON)
            .syncBody(person)
            .retrieve()
            .bodyToMono(Void.class);

Builder options

A simple way to create WebClient is through the static factory methods create() and create(String) with a base URL for all requests. You can also use WebClient.builder() for access to more options.

To customize the underlying HTTP client:

    SslContext sslContext = ...

    ClientHttpConnector connector = new ReactorClientHttpConnector(
            builder -> builder.sslContext(sslContext));

    WebClient webClient = WebClient.builder()
            .clientConnector(connector)
            .build();

To customize the HTTP codecs used for encoding and decoding HTTP messages:

    ExchangeStrategies strategies = ExchangeStrategies.builder()
            .codecs(configurer -> {
                // ...
            })
            .build();

    WebClient webClient = WebClient.builder()
            .exchangeStrategies(strategies)
            .build();

The builder can be used to insert Filters.

Explore the WebClient.Builder in your IDE for other options related to URI building, default headers (and cookies), and more.

After the WebClient is built, you can always obtain a new builder from it, in order to build a new WebClient, based on, but without affecting the current instance:

    WebClient modifiedClient = client.mutate()
            // user builder methods...
            .build();

Filters

WebClient supports interception style request filtering:

    WebClient client = WebClient.builder()
        .filter((request, next) -> {

    	    ClientRequest filtered = ClientRequest.from(request)
        	    .header("foo", "bar")
        	    .build();

        	return next.exchange(filtered);
        })
        .build();

ExchangeFilterFunctions provides a filter for basic authentication:

// static import of ExchangeFilterFunctions.basicAuthentication

    WebClient client = WebClient.builder()
        .filter(basicAuthentication("user", "pwd"))
        .build();

You can also mutate an existing WebClient instance without affecting the original:

    WebClient filteredClient = client.mutate()
            .filter(basicAuthentication("user", "pwd")
            .build();