The RestClient
is a synchronous HTTP client that offers a modern, fluent API. It offers an abstraction over HTTP libraries that allows for convenient conversion from a Java object to an HTTP request, and the creation of objects from an HTTP response.
Creating a RestClient
The RestClient
is created using one of the static create
methods. You can also use builder()
to get a builder with further options, such as specifying which HTTP library to use and which message converters to use, setting a default URI, default path variables, default request headers, or uriBuilderFactory
, or registering interceptors and initializers.
Once created (or built), the RestClient
can be used safely by multiple threads.
The following sample shows how to create a default RestClient
, and how to build a custom one.
RestClient defaultClient = RestClient.create(); RestClient customClient = RestClient.builder() .requestFactory(new HttpComponentsClientHttpRequestFactory()) .messageConverters(converters -> converters.add(new MyCustomMessageConverter())) .baseUrl("https://example.com") .defaultUriVariables(Map.of("variable", "foo")) .defaultHeader("My-Header", "Foo") .defaultCookie("My-Cookie", "Bar") .requestInterceptor(myCustomInterceptor) .requestInitializer(myCustomInitializer) .build();
Using the RestClient
When making an HTTP request with the RestClient
, the first thing to specify is which HTTP method to use. This can be done with method(HttpMethod)
or with the convenience methods get()
, head()
, post()
, and so on.
Request URL
Next, the request URI can be specified with the uri
methods. This step is optional and can be skipped if the RestClient
is configured with a default URI. The URL is typically specified as a String
, with optional URI template variables. The following example configures a GET request to example.com/orders/42
:
int id = 42; restClient.get() .uri("https://example.com/orders/{id}", id) ....
A function can also be used for more controls, such as specifying request parameters.
String URLs are encoded by default, but this can be changed by building a client with a custom uriBuilderFactory
. The URL can also be provided with a function or as a java.net.URI
, both of which are not encoded.
Request headers and body
If necessary, the HTTP request can be manipulated by adding request headers with header(String, String)
, headers(Consumer<HttpHeaders>
, or with the convenience methods accept(MediaType…)
, acceptCharset(Charset…)
and so on. For HTTP requests that can contain a body (POST
, PUT
, and PATCH
), additional methods are available: contentType(MediaType)
, and contentLength(long)
.
The request body itself can be set by body(Object)
, which internally uses HTTP Message Conversion. Alternatively, the request body can be set using a ParameterizedTypeReference
, allowing you to use generics. Finally, the body can be set to a callback function that writes to an OutputStream
.
Retrieving the response
Once the request has been set up, it can be sent by chaining method calls after retrieve()
. For example, the response body can be accessed by using retrieve().body(Class)
or retrieve().body(ParameterizedTypeReference)
for parameterized types like lists. The body
method converts the response contents into various types – for instance, bytes can be converted into a String
, JSON can be converted into objects using Jackson, and so on.
The response can also be converted into a ResponseEntity
, giving access to the response headers as well as the body, with retrieve().toEntity(Class)
.
Calling retrieve()
by itself is a no-op and returns a ResponseSpec
. Applications must invoke a terminal operation on the ResponseSpec
to have any side effect. If consuming the response has no interest for your use case, you can use retrieve().toBodilessEntity()
.
This sample shows how RestClient
can be used to perform a simple GET
request.
String result = restClient.get() .uri("https://example.com") .retrieve() .body(String.class); System.out.println(result);
Access to the response status code and headers is provided through ResponseEntity
:
ResponseEntity<String> result = restClient.get() .uri("https://example.com") .retrieve() .toEntity(String.class); System.out.println("Response status: " + result.getStatusCode()); System.out.println("Response headers: " + result.getHeaders()); System.out.println("Contents: " + result.getBody());
RestClient
can convert JSON to objects, using the Jackson library. Note the usage of URI variables in this sample and that the Accept
header is set to JSON.
int id = ...; Pet pet = restClient.get() .uri("https://petclinic.example.com/pets/{id}", id) .accept(APPLICATION_JSON) .retrieve() .body(Pet.class);
In the next sample, RestClient
is used to perform a POST request that contains JSON, which again is converted using Jackson.
Pet pet = ... ResponseEntity<Void> response = restClient.post() .uri("https://petclinic.example.com/pets/new") .contentType(APPLICATION_JSON) .body(pet) .retrieve() .toBodilessEntity();
Error handling
By default, RestClient
throws a subclass of RestClientException
when retrieving a response with a 4xx or 5xx status code. This behavior can be overridden using onStatus
.
String result = restClient.get() .uri("https://example.com/this-url-does-not-exist") .retrieve() .onStatus(HttpStatusCode::is4xxClientError, (request, response) -> { throw new MyCustomRuntimeException(response.getStatusCode(), response.getHeaders()); }) .body(String.class);
Exchange
For more advanced scenarios, the RestClient
gives access to the underlying HTTP request and response through the exchange()
method, which can be used instead of retrieve()
. Status handlers are not applied when use exchange()
, because the exchange function already provides access to the full response, allowing you to perform any error handling necessary.
Pet result = restClient.get() .uri("https://petclinic.example.com/pets/{id}", id) .accept(APPLICATION_JSON) .exchange((request, response) -> { if (response.getStatusCode().is4xxClientError()) { throw new MyCustomRuntimeException(response.getStatusCode(), response.getHeaders()); } else { Pet pet = convertResponse(response); return pet; } });
HTTP Message Conversion
Jackson JSON Views
To serialize only a subset of the object properties, you can specify a Jackson JSON View, as the following example shows:
MappingJacksonValue value = new MappingJacksonValue(new User("eric", "7!jd#h23")); value.setSerializationView(User.WithoutPasswordView.class); ResponseEntity<Void> response = restClient.post() // or RestTemplate.postForEntity .contentType(APPLICATION_JSON) .body(value) .retrieve() .toBodilessEntity();
Multipart
To send multipart data, you need to provide a MultiValueMap<String, Object>
whose values may be an Object
for part content, a Resource
for a file part, or an HttpEntity
for part content with headers. For example:
MultiValueMap<String, Object> parts = new LinkedMultiValueMap<>(); parts.add("fieldPart", "fieldValue"); parts.add("filePart", new FileSystemResource("...logo.png")); parts.add("jsonPart", new Person("Jason")); HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_XML); parts.add("xmlPart", new HttpEntity<>(myBean, headers)); // send using RestClient.post or RestTemplate.postForEntity
In most cases, you do not have to specify the Content-Type
for each part. The content type is determined automatically based on the HttpMessageConverter
chosen to serialize it or, in the case of a Resource
, based on the file extension. If necessary, you can explicitly provide the MediaType
with an HttpEntity
wrapper.
Once the MultiValueMap
is ready, you can use it as the body of a POST
request, using RestClient.post().body(parts)
(or RestTemplate.postForObject
).
If the MultiValueMap
contains at least one non-String
value, the Content-Type
is set to multipart/form-data
by the FormHttpMessageConverter
. If the MultiValueMap
has String
values, the Content-Type
defaults to application/x-www-form-urlencoded
. If necessary the Content-Type
may also be set explicitly.
Client Request Factories
To execute the HTTP request, RestClient
uses a client HTTP library. These libraries are adapted via the ClientRequestFactory
interface. Various implementations are available:
JdkClientHttpRequestFactory
for Java’sHttpClient
HttpComponentsClientHttpRequestFactory
for use with Apache HTTP ComponentsHttpClient
JettyClientHttpRequestFactory
for Jetty’sHttpClient
ReactorNettyClientRequestFactory
for Reactor Netty’sHttpClient
SimpleClientHttpRequestFactory
as a simple default
If no request factory is specified when the RestClient
was built, it will use the Apache or Jetty HttpClient
if they are available on the classpath. Otherwise, if the java.net.http
module is loaded, it will use Java’s HttpClient
. Finally, it will resort to the simple default.
Note that the SimpleClientHttpRequestFactory
may raise an exception when accessing the status of a response that represents an error (for example, 401). If this is an issue, use any of the alternative request factories.