WebClient is Spring WebFlux's non-blocking, reactive HTTP client for making requests to external APIs. It provides a fluent API for building requests, handling responses, and applying filters for cross-cutting concerns like authentication, logging, and error handling.
Factory methods for creating WebClient instances.
public interface WebClient {
// Create a WebClient with default settings
static WebClient create() { ... }
// Create a WebClient with a base URL
static WebClient create(String baseUrl) { ... }
// Create a WebClient builder for customization
static WebClient.Builder builder() { ... }
// Create a mutated copy of this WebClient
Builder mutate();
}Usage:
import org.springframework.web.reactive.function.client.WebClient;
// Simple creation
WebClient client = WebClient.create("https://api.example.com");
// Using builder for customization
WebClient client = WebClient.builder()
.baseUrl("https://api.example.com")
.defaultHeader("User-Agent", "My App")
.defaultCookie("session", "...")
.build();Builder interface for configuring WebClient instances.
interface Builder {
// Set base URL for requests
Builder baseUrl(String baseUrl);
// Set default URI variables
Builder defaultUriVariables(Map<String, ?> defaultUriVariables);
// Set default headers for all requests
Builder defaultHeader(String header, String... values);
Builder defaultHeaders(Consumer<HttpHeaders> headersConsumer);
// Set default cookies for all requests
Builder defaultCookie(String cookie, String... values);
Builder defaultCookies(Consumer<MultiValueMap<String, String>> cookiesConsumer);
// Add default headers/cookies from Consumer
Builder defaultRequest(Consumer<WebClient.RequestHeadersSpec<?>> defaultRequest);
// Add filter for request/response processing
Builder filter(ExchangeFilterFunction filter);
Builder filters(Consumer<List<ExchangeFilterFunction>> filtersConsumer);
// Configure HTTP client connector
Builder clientConnector(ClientHttpConnector connector);
// Configure message readers/writers
Builder codecs(Consumer<ClientCodecConfigurer> configurer);
// Configure exchange strategies
Builder exchangeStrategies(ExchangeStrategies strategies);
// Configure exchange function
Builder exchangeFunction(ExchangeFunction exchangeFunction);
// Apply initialization function
Builder apply(Consumer<Builder> builderConsumer);
// Clone this builder
Builder clone();
// Build the WebClient
WebClient build();
}Usage:
WebClient client = WebClient.builder()
.baseUrl("https://api.example.com")
.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.defaultHeader(HttpHeaders.AUTHORIZATION, "Bearer " + token)
.filter(ExchangeFilterFunctions.basicAuthentication("user", "password"))
.codecs(configurer -> configurer.defaultCodecs().maxInMemorySize(16 * 1024 * 1024))
.build();Methods for initiating HTTP requests with different HTTP methods.
public interface WebClient {
// HTTP GET request
RequestHeadersUriSpec<?> get();
// HTTP POST request
RequestBodyUriSpec post();
// HTTP PUT request
RequestBodyUriSpec put();
// HTTP PATCH request
RequestBodyUriSpec patch();
// HTTP DELETE request
RequestHeadersUriSpec<?> delete();
// HTTP HEAD request
RequestHeadersUriSpec<?> head();
// HTTP OPTIONS request
RequestHeadersUriSpec<?> options();
// HTTP request with custom method
RequestBodyUriSpec method(HttpMethod method);
}Usage:
// GET request
Mono<String> result = client.get()
.uri("/users/{id}", userId)
.retrieve()
.bodyToMono(String.class);
// POST request
Mono<User> created = client.post()
.uri("/users")
.bodyValue(newUser)
.retrieve()
.bodyToMono(User.class);
// PUT request
Mono<Void> updated = client.put()
.uri("/users/{id}", userId)
.bodyValue(updatedUser)
.retrieve()
.bodyToMono(Void.class);
// DELETE request
Mono<Void> deleted = client.delete()
.uri("/users/{id}", userId)
.retrieve()
.bodyToMono(Void.class);Interface for specifying the request URI.
interface UriSpec<S extends RequestHeadersSpec<?>> {
// URI as String
S uri(String uri, Object... uriVariables);
S uri(String uri, Map<String, ?> uriVariables);
// URI using UriBuilder
S uri(Function<UriBuilder, URI> uriFunction);
// URI as java.net.URI
S uri(URI uri);
}interface RequestHeadersUriSpec<S extends RequestHeadersSpec<S>> extends UriSpec<S>, RequestHeadersSpec<S> { }interface RequestBodyUriSpec extends RequestBodySpec, RequestHeadersUriSpec<RequestBodySpec> { }Usage:
// URI with path variables
client.get()
.uri("/users/{id}/orders/{orderId}", userId, orderId)
.retrieve();
// URI with query parameters using UriBuilder
client.get()
.uri(uriBuilder -> uriBuilder
.path("/users")
.queryParam("page", 1)
.queryParam("size", 20)
.build())
.retrieve();Interface for configuring request headers.
interface RequestHeadersSpec<S extends RequestHeadersSpec<S>> extends RequestHeadersUriSpec<S> {
// Set Accept header
S accept(MediaType... acceptableMediaTypes);
// Set Accept-Charset header
S acceptCharset(Charset... acceptableCharsets);
// Set If-Modified-Since header
S ifModifiedSince(ZonedDateTime ifModifiedSince);
// Set If-None-Match header
S ifNoneMatch(String... ifNoneMatches);
// Set custom header
S header(String headerName, String... headerValues);
// Set headers using Consumer
S headers(Consumer<HttpHeaders> headersConsumer);
// Set request attribute
S attribute(String name, Object value);
// Set attributes using Consumer
S attributes(Consumer<Map<String, Object>> attributesConsumer);
// Set cookie
S cookie(String name, String value);
// Set cookies using Consumer
S cookies(Consumer<MultiValueMap<String, String>> cookiesConsumer);
// Retrieve response
ResponseSpec retrieve();
// Exchange for full control over request and response
Mono<ClientResponse> exchange();
// Exchange to Mono
<V> Mono<V> exchangeToMono(Function<ClientResponse, ? extends Mono<V>> responseHandler);
// Exchange to Flux
<V> Flux<V> exchangeToFlux(Function<ClientResponse, ? extends Flux<V>> responseHandler);
}Usage:
client.get()
.uri("/data")
.accept(MediaType.APPLICATION_JSON)
.header("X-Custom-Header", "value")
.headers(headers -> {
headers.setBearerAuth(token);
headers.set("X-Request-ID", requestId);
})
.cookie("session", sessionId)
.retrieve()
.bodyToMono(String.class);Interface for configuring request body for POST, PUT, PATCH requests.
interface RequestBodySpec extends RequestHeadersSpec<RequestBodySpec> {
// Set Content-Length header
RequestBodySpec contentLength(long contentLength);
// Set Content-Type header
RequestBodySpec contentType(MediaType contentType);
// Set request body from Object
RequestHeadersSpec<?> bodyValue(Object body);
// Set request body from Publisher (Mono or Flux)
<T, P extends Publisher<T>> RequestHeadersSpec<?> body(P publisher, Class<T> elementClass);
<T, P extends Publisher<T>> RequestHeadersSpec<?> body(P publisher, ParameterizedTypeReference<T> elementTypeRef);
// Set request body using BodyInserter
RequestHeadersSpec<?> body(BodyInserter<?, ? super ClientHttpRequest> inserter);
}Usage:
// Simple body value
client.post()
.uri("/users")
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(user)
.retrieve();
// Publisher body (Mono)
Mono<User> userMono = ...;
client.post()
.uri("/users")
.body(userMono, User.class)
.retrieve();
// Publisher body (Flux)
Flux<User> usersFlux = ...;
client.post()
.uri("/users/batch")
.body(usersFlux, User.class)
.retrieve();
// Multipart form data
client.post()
.uri("/upload")
.body(BodyInserters.fromMultipartData("file", fileResource)
.with("description", "My file"))
.retrieve();Interface for handling responses and extracting body.
interface ResponseSpec {
// Handle specific status codes
ResponseSpec onStatus(Predicate<HttpStatusCode> statusPredicate, Function<ClientResponse, Mono<? extends Throwable>> exceptionFunction);
// Extract body as Mono
<T> Mono<T> bodyToMono(Class<T> elementClass);
<T> Mono<T> bodyToMono(ParameterizedTypeReference<T> elementTypeRef);
// Extract body as Flux
<T> Flux<T> bodyToFlux(Class<T> elementClass);
<T> Flux<T> bodyToFlux(ParameterizedTypeReference<T> elementTypeRef);
// Convert to bodiless entity
Mono<ResponseEntity<Void>> toBodilessEntity();
// Convert to ResponseEntity with body
<T> Mono<ResponseEntity<T>> toEntity(Class<T> bodyType);
<T> Mono<ResponseEntity<T>> toEntity(ParameterizedTypeReference<T> bodyTypeReference);
// Convert to ResponseEntity with List body
<T> Mono<ResponseEntity<List<T>>> toEntityList(Class<T> elementType);
<T> Mono<ResponseEntity<List<T>>> toEntityList(ParameterizedTypeReference<T> elementTypeRef);
// Convert to ResponseEntity with Flux body
<T> ResponseEntity<Flux<T>> toEntityFlux(Class<T> elementType);
<T> ResponseEntity<Flux<T>> toEntityFlux(ParameterizedTypeReference<T> elementTypeRef);
}Usage:
// Extract body as Mono
Mono<User> user = client.get()
.uri("/users/{id}", userId)
.retrieve()
.bodyToMono(User.class);
// Extract body as Flux
Flux<User> users = client.get()
.uri("/users")
.retrieve()
.bodyToFlux(User.class);
// Handle specific errors
Mono<User> user = client.get()
.uri("/users/{id}", userId)
.retrieve()
.onStatus(HttpStatusCode::is4xxClientError,
response -> Mono.error(new NotFoundException("User not found")))
.onStatus(HttpStatusCode::is5xxServerError,
response -> Mono.error(new ServerException("Server error")))
.bodyToMono(User.class);
// Get ResponseEntity with body
Mono<ResponseEntity<User>> entity = client.get()
.uri("/users/{id}", userId)
.retrieve()
.toEntity(User.class);Apply filters for cross-cutting concerns like authentication, logging, and error handling.
@FunctionalInterface
public interface ExchangeFilterFunction {
Mono<ClientResponse> filter(ClientRequest request, ExchangeFunction next);
// Chain filters
default ExchangeFilterFunction andThen(ExchangeFilterFunction afterFilter) { ... }
// Apply filter to ExchangeFunction
default ExchangeFunction apply(ExchangeFunction exchange) { ... }
}Static factory methods for common filters:
public class ExchangeFilterFunctions {
// Basic authentication
public static ExchangeFilterFunction basicAuthentication(String username, String password) { ... }
// Handle status codes
public static ExchangeFilterFunction statusError(Predicate<HttpStatusCode> statusPredicate,
Function<ClientResponse, ? extends Throwable> exceptionFunction) { ... }
// Limit response size
public static ExchangeFilterFunction limitResponseSize(long maxByteCount) { ... }
}Custom filter example:
ExchangeFilterFunction loggingFilter = (request, next) -> {
System.out.println("Request: " + request.method() + " " + request.url());
return next.exchange(request)
.doOnNext(response -> System.out.println("Response: " + response.statusCode()));
};
WebClient client = WebClient.builder()
.filter(loggingFilter)
.filter(ExchangeFilterFunctions.basicAuthentication("user", "password"))
.build();Configure message readers and writers for request/response body conversion.
public interface ExchangeStrategies {
// Get message readers
List<HttpMessageReader<?>> messageReaders();
// Get message writers
List<HttpMessageWriter<?>> messageWriters();
// Create default strategies
static ExchangeStrategies withDefaults() { ... }
// Create builder
static Builder builder() { ... }
// Mutate existing strategies
Builder mutate();
interface Builder {
// Configure default codecs
Builder codecs(Consumer<ClientCodecConfigurer> configurer);
// Build strategies
ExchangeStrategies build();
}
}Usage:
ExchangeStrategies strategies = ExchangeStrategies.builder()
.codecs(configurer -> {
configurer.defaultCodecs().maxInMemorySize(16 * 1024 * 1024);
configurer.defaultCodecs().enableLoggingRequestDetails(true);
})
.build();
WebClient client = WebClient.builder()
.exchangeStrategies(strategies)
.build();Low-level request and response types for use with exchange() method.
public interface ClientRequest {
HttpMethod method();
URI url();
HttpHeaders headers();
MultiValueMap<String, String> cookies();
BodyInserter<?, ? super ClientHttpRequest> body();
Map<String, Object> attributes();
Consumer<ClientHttpRequest> httpRequest();
static Builder create(HttpMethod method, URI url) { ... }
Builder mutate();
interface Builder {
Builder method(HttpMethod method);
Builder url(URI url);
Builder header(String headerName, String... headerValues);
Builder headers(Consumer<HttpHeaders> headersConsumer);
Builder cookie(String name, String value);
Builder cookies(Consumer<MultiValueMap<String, String>> cookiesConsumer);
Builder body(BodyInserter<?, ? super ClientHttpRequest> inserter);
Builder attribute(String name, Object value);
Builder attributes(Consumer<Map<String, Object>> attributesConsumer);
Builder httpRequest(Consumer<ClientHttpRequest> requestConsumer);
ClientRequest build();
}
}public interface ClientResponse {
HttpStatusCode statusCode();
int rawStatusCode();
HttpHeaders headers();
MultiValueMap<String, ResponseCookie> cookies();
// Extract body
<T> T body(BodyExtractor<T, ? super ClientHttpResponse> extractor);
<T> Mono<T> bodyToMono(Class<? extends T> elementClass);
<T> Mono<T> bodyToMono(ParameterizedTypeReference<T> elementTypeRef);
<T> Flux<T> bodyToFlux(Class<? extends T> elementClass);
<T> Flux<T> bodyToFlux(ParameterizedTypeReference<T> elementTypeRef);
// Convert to entity
Mono<ResponseEntity<Void>> toBodilessEntity();
<T> Mono<ResponseEntity<T>> toEntity(Class<T> bodyType);
<T> Mono<ResponseEntity<T>> toEntity(ParameterizedTypeReference<T> bodyTypeRef);
<T> Mono<ResponseEntity<List<T>>> toEntityList(Class<T> elementType);
<T> Mono<ResponseEntity<List<T>>> toEntityList(ParameterizedTypeReference<T> elementTypeRef);
// Release body without consuming
Mono<Void> releaseBody();
// Create error from response status
<T> Mono<T> createError();
ExchangeStrategies strategies();
Builder mutate();
interface Headers {
OptionalLong contentLength();
Optional<MediaType> contentType();
List<String> header(String headerName);
HttpHeaders asHttpHeaders();
}
interface Builder { ... }
}Usage with exchange:
Mono<String> result = client.get()
.uri("/data")
.exchangeToMono(response -> {
if (response.statusCode().is2xxSuccessful()) {
return response.bodyToMono(String.class);
} else {
return response.createError();
}
});// Base exception for WebClient errors
public class WebClientException extends NestedRuntimeException {
public WebClientException(String msg) { ... }
public WebClientException(String msg, Throwable cause) { ... }
}// Exception for I/O errors during request
public class WebClientRequestException extends WebClientException {
public WebClientRequestException(Throwable ex, HttpMethod method, URI uri, HttpHeaders headers) { ... }
public HttpMethod getMethod() { ... }
public URI getUri() { ... }
public HttpHeaders getHeaders() { ... }
}// Exception for HTTP error status codes
public class WebClientResponseException extends WebClientException {
public WebClientResponseException(String message, int statusCode, String statusText,
HttpHeaders headers, byte[] responseBody, Charset responseCharset) { ... }
public HttpStatusCode getStatusCode() { ... }
public int getRawStatusCode() { ... }
public String getStatusText() { ... }
public HttpHeaders getHeaders() { ... }
public byte[] getResponseBodyAsByteArray() { ... }
public String getResponseBodyAsString() { ... }
public <T> T getResponseBodyAs(Class<T> targetType) { ... }
// Specific status code exceptions
public static class BadRequest extends WebClientResponseException { ... }
public static class Unauthorized extends WebClientResponseException { ... }
public static class Forbidden extends WebClientResponseException { ... }
public static class NotFound extends WebClientResponseException { ... }
public static class MethodNotAllowed extends WebClientResponseException { ... }
public static class NotAcceptable extends WebClientResponseException { ... }
public static class Conflict extends WebClientResponseException { ... }
public static class Gone extends WebClientResponseException { ... }
public static class UnsupportedMediaType extends WebClientResponseException { ... }
public static class TooManyRequests extends WebClientResponseException { ... }
public static class InternalServerError extends WebClientResponseException { ... }
public static class BadGateway extends WebClientResponseException { ... }
public static class ServiceUnavailable extends WebClientResponseException { ... }
public static class GatewayTimeout extends WebClientResponseException { ... }
}// Exception for unknown HTTP status codes
public class UnknownHttpStatusCodeException extends WebClientResponseException {
public UnknownHttpStatusCodeException(int statusCode, HttpHeaders headers, byte[] responseBody, Charset charset) { ... }
}