Reference documentation for `RestClient`
In addition to providing reference documentation for the `RestClient`, this commit also shortens the `RestTemplate` section. Closes gh-30826
This commit is contained in:
parent
e42902b742
commit
7b5effecf3
|
|
@ -12,9 +12,457 @@ The Spring Framework provides the following choices for making calls to REST end
|
||||||
[[rest-restclient]]
|
[[rest-restclient]]
|
||||||
== `RestClient`
|
== `RestClient`
|
||||||
|
|
||||||
Reference documentation is forthcoming.
|
The `RestClient` is a synchronous HTTP client that offers a modern, fluent API.
|
||||||
For now, please refer to the https://docs.spring.io/spring-framework/docs/6.1.0-M2/javadoc-api/org/springframework/web/client/RestClient.html[API documentation].
|
It offers an abstraction over HTTP libraries that allows for convenient conversion from Java object to HTTP request, and creation of objects from the 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 (see <<rest-request-factories>>) and which message converters to use (see <<rest-message-conversion>>), setting a default URI, default path variables, a 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.
|
||||||
|
|
||||||
|
[tabs]
|
||||||
|
======
|
||||||
|
Java::
|
||||||
|
+
|
||||||
|
[source,java,indent=0,subs="verbatim",role="primary"]
|
||||||
|
----
|
||||||
|
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")
|
||||||
|
.requestInterceptor(myCustomInterceptor)
|
||||||
|
.requestInitializer(myCustomInitializer)
|
||||||
|
.build();
|
||||||
|
----
|
||||||
|
|
||||||
|
Kotlin::
|
||||||
|
+
|
||||||
|
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
|
||||||
|
----
|
||||||
|
val defaultClient = RestClient.create()
|
||||||
|
|
||||||
|
val customClient = RestClient.builder()
|
||||||
|
.requestFactory(HttpComponentsClientHttpRequestFactory())
|
||||||
|
.messageConverters(converters -> converters.add(MyCustomMessageConverter()))
|
||||||
|
.baseUrl("https://example.com")
|
||||||
|
.defaultUriVariables(Map.of("variable", "foo"))
|
||||||
|
.defaultHeader("My-Header", "Foo")
|
||||||
|
.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 `String`, with optional URI template variables.
|
||||||
|
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 `java.net.URI`, both of which are not encoded.
|
||||||
|
For more details on working with and encoding URIs, see xref:web/webmvc/mvc-uri-building.adoc[URI Links].
|
||||||
|
|
||||||
|
==== 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 request 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 <<rest-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, the HTTP response is accessed by invoking `retrieve()`.
|
||||||
|
The response body can be accessed by using `body(Class)`, or `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 into objects using Jackson, and so on (see <<rest-message-conversion>>).
|
||||||
|
|
||||||
|
The response can also be converted into a `ResponseEntity`, giving access to the response headers as well as the body.
|
||||||
|
|
||||||
|
This sample shows how `RestClient` can be used to perform a simple GET request.
|
||||||
|
|
||||||
|
[tabs]
|
||||||
|
======
|
||||||
|
Java::
|
||||||
|
+
|
||||||
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||||
|
----
|
||||||
|
String result = restClient.get() <1>
|
||||||
|
.uri("https://example.com") <2>
|
||||||
|
.retrieve() <3>
|
||||||
|
.body(String.class); <4>
|
||||||
|
|
||||||
|
System.out.println(result); <5>
|
||||||
|
----
|
||||||
|
<1> Set up a GET request
|
||||||
|
<2> Specify the URL to connect to
|
||||||
|
<3> Retrieve the response
|
||||||
|
<4> Convert the response into a string
|
||||||
|
<5> Print the result
|
||||||
|
|
||||||
|
Kotlin::
|
||||||
|
+
|
||||||
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||||
|
----
|
||||||
|
val result= restClient.get() <1>
|
||||||
|
.uri("https://example.com") <2>
|
||||||
|
.retrieve() <3>
|
||||||
|
.body<String>() <4>
|
||||||
|
|
||||||
|
println(result) <5>
|
||||||
|
----
|
||||||
|
<1> Set up a GET request
|
||||||
|
<2> Specify the URL to connect to
|
||||||
|
<3> Retrieve the response
|
||||||
|
<4> Convert the response into a string
|
||||||
|
<5> Print the result
|
||||||
|
======
|
||||||
|
|
||||||
|
Access to the response status code and headers is provided through `ResponseEntity`:
|
||||||
|
|
||||||
|
[tabs]
|
||||||
|
======
|
||||||
|
Java::
|
||||||
|
+
|
||||||
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||||
|
----
|
||||||
|
ResponseEntity<String> result = restClient.get() <1>
|
||||||
|
.uri("https://example.com") <1>
|
||||||
|
.retrieve()
|
||||||
|
.toEntity(String.class); <2>
|
||||||
|
|
||||||
|
System.out.println("Response status: " + result.getStatusCode()); <3>
|
||||||
|
System.out.println("Response headers: " + result.getHeaders()); <3>
|
||||||
|
System.out.println("Contents: " + result.getBody()); <3>
|
||||||
|
----
|
||||||
|
<1> Set up a GET request for the specified URL
|
||||||
|
<2> Convert the response into a `ResponseEntity`
|
||||||
|
<3> Print the result
|
||||||
|
|
||||||
|
Kotlin::
|
||||||
|
+
|
||||||
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||||
|
----
|
||||||
|
val result = restClient.get() <1>
|
||||||
|
.uri("https://example.com") <1>
|
||||||
|
.retrieve()
|
||||||
|
.toEntity<String>() <2>
|
||||||
|
|
||||||
|
println("Response status: " + result.statusCode) <3>
|
||||||
|
println("Response headers: " + result.headers) <3>
|
||||||
|
println("Contents: " + result.body) <3>
|
||||||
|
----
|
||||||
|
<1> Set up a GET request for the specified URL
|
||||||
|
<2> Convert the response into a `ResponseEntity`
|
||||||
|
<3> Print the result
|
||||||
|
======
|
||||||
|
|
||||||
|
`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.
|
||||||
|
|
||||||
|
[tabs]
|
||||||
|
======
|
||||||
|
Java::
|
||||||
|
+
|
||||||
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||||
|
----
|
||||||
|
int id = ...;
|
||||||
|
Pet pet = restClient.get()
|
||||||
|
.uri("https://petclinic.example.com/pets/{id}", id) <1>
|
||||||
|
.accept(APPLICATION_JSON) <2>
|
||||||
|
.retrieve()
|
||||||
|
.body(Pet.class); <3>
|
||||||
|
----
|
||||||
|
<1> Using URI variables
|
||||||
|
<2> Set the `Accept` header to `application/json`
|
||||||
|
<3> Convert the JSON response into a `Pet` domain object
|
||||||
|
|
||||||
|
Kotlin::
|
||||||
|
+
|
||||||
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||||
|
----
|
||||||
|
val id = ...
|
||||||
|
val pet = restClient.get()
|
||||||
|
.uri("https://petclinic.example.com/pets/{id}", id) <1>
|
||||||
|
.accept(APPLICATION_JSON) <2>
|
||||||
|
.retrieve()
|
||||||
|
.body<Pet>() <3>
|
||||||
|
----
|
||||||
|
<1> Using URI variables
|
||||||
|
<2> Set the `Accept` header to `application/json`
|
||||||
|
<3> Convert the JSON response into a `Pet` domain object
|
||||||
|
======
|
||||||
|
|
||||||
|
In the next sample, `RestClient` is used to perform a POST request that contains JSON, which again is converted using Jackson.
|
||||||
|
|
||||||
|
[tabs]
|
||||||
|
======
|
||||||
|
Java::
|
||||||
|
+
|
||||||
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||||
|
----
|
||||||
|
Pet pet = ... <1>
|
||||||
|
ResponseEntity<Void> response = restClient.post() <2>
|
||||||
|
.uri("https://petclinic.example.com/pets/new") <2>
|
||||||
|
.contentType(APPLICATION_JSON) <3>
|
||||||
|
.body(pet) <4>
|
||||||
|
.retrieve()
|
||||||
|
.toBodilessEntity(); <5>
|
||||||
|
----
|
||||||
|
<1> Create a `Pet` domain object
|
||||||
|
<2> Set up a POST request, and the URL to connect to
|
||||||
|
<3> Set the `Content-Type` header to `application/json`
|
||||||
|
<4> Use `pet` as the request body
|
||||||
|
<5> Convert the response into a response entity with no body.
|
||||||
|
|
||||||
|
Kotlin::
|
||||||
|
+
|
||||||
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||||
|
----
|
||||||
|
val pet: Pet = ... <1>
|
||||||
|
val response = restClient.post() <2>
|
||||||
|
.uri("https://petclinic.example.com/pets/new") <2>
|
||||||
|
.contentType(APPLICATION_JSON) <3>
|
||||||
|
.body(pet) <4>
|
||||||
|
.retrieve()
|
||||||
|
.toBodilessEntity() <5>
|
||||||
|
----
|
||||||
|
<1> Create a `Pet` domain object
|
||||||
|
<2> Set up a POST request, and the URL to connect to
|
||||||
|
<3> Set the `Content-Type` header to `application/json`
|
||||||
|
<4> Use `pet` as the request body
|
||||||
|
<5> Convert the response into a response entity with no body.
|
||||||
|
======
|
||||||
|
|
||||||
|
==== 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 overriden using `onStatus`.
|
||||||
|
|
||||||
|
[tabs]
|
||||||
|
======
|
||||||
|
Java::
|
||||||
|
+
|
||||||
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||||
|
----
|
||||||
|
String result = restClient.get() <1>
|
||||||
|
.uri("https://example.com/this-url-does-not-exist") <1>
|
||||||
|
.retrieve()
|
||||||
|
.onStatus(HttpStatusCode::is4xxClientError, (request, response) -> { <2>
|
||||||
|
throw new MyCustomRuntimeException(response.getStatusCode(), response.getHeaders()) <3>
|
||||||
|
})
|
||||||
|
.body(String.class);
|
||||||
|
----
|
||||||
|
<1> Create a GET request for a URL that returns a 404 status code
|
||||||
|
<2> Set up a status handler for all 4xx status codes
|
||||||
|
<3> Throw a custom exception
|
||||||
|
|
||||||
|
Kotlin::
|
||||||
|
+
|
||||||
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||||
|
----
|
||||||
|
val result = restClient.get() <1>
|
||||||
|
.uri("https://example.com/this-url-does-not-exist") <1>
|
||||||
|
.retrieve()
|
||||||
|
.onStatus(HttpStatusCode::is4xxClientError) { _, response -> <2>
|
||||||
|
throw MyCustomRuntimeException(response.getStatusCode(), response.getHeaders()) } <3>
|
||||||
|
.body<String>()
|
||||||
|
----
|
||||||
|
<1> Create a GET request for a URL that returns a 404 status code
|
||||||
|
<2> Set up a status handler for all 4xx status codes
|
||||||
|
<3> Throw a custom exception
|
||||||
|
======
|
||||||
|
|
||||||
|
==== 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 you exchange, because the exchange function already provides access to the full response, allowing you to perform any error handling necessary.
|
||||||
|
|
||||||
|
[tabs]
|
||||||
|
======
|
||||||
|
Java::
|
||||||
|
+
|
||||||
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||||
|
----
|
||||||
|
Pet result = restClient.get()
|
||||||
|
.uri("https://petclinic.example.com/pets/{id}", id)
|
||||||
|
.accept(APPLICATION_JSON)
|
||||||
|
.exchange((request, response) -> { <1>
|
||||||
|
if (response.getStatusCode().is4xxClientError()) { <2>
|
||||||
|
throw new MyCustomRuntimeException(response.getStatusCode(), response.getHeaders()); <2>
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Pet pet = convertResponse(response); <3>
|
||||||
|
return pet;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
----
|
||||||
|
<1> `exchange` provides the request and response
|
||||||
|
<2> Throw an exception when the response has a 4xx status code
|
||||||
|
<3> Convert the response into a Pet domain object
|
||||||
|
|
||||||
|
Kotlin::
|
||||||
|
+
|
||||||
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||||
|
----
|
||||||
|
val result = restClient.get()
|
||||||
|
.uri("https://petclinic.example.com/pets/{id}", id)
|
||||||
|
.accept(MediaType.APPLICATION_JSON)
|
||||||
|
.exchange { request, response -> <1>
|
||||||
|
if (response.getStatusCode().is4xxClientError()) { <2>
|
||||||
|
throw MyCustomRuntimeException(response.getStatusCode(), response.getHeaders()) <2>
|
||||||
|
} else {
|
||||||
|
val pet: Pet = convertResponse(response) <3>
|
||||||
|
pet
|
||||||
|
}
|
||||||
|
}
|
||||||
|
----
|
||||||
|
<1> `exchange` provides the request and response
|
||||||
|
<2> Throw an exception when the response has a 4xx status code
|
||||||
|
<3> Convert the response into a Pet domain object
|
||||||
|
======
|
||||||
|
|
||||||
|
|
||||||
|
[[rest-message-conversion]]
|
||||||
|
=== HTTP Message Conversion
|
||||||
|
[.small]#xref:web/webflux/reactive-spring.adoc#webflux-codecs[See equivalent in the Reactive stack]#
|
||||||
|
|
||||||
|
The `spring-web` module contains the `HttpMessageConverter` interface for reading and writing the body of HTTP requests and responses through `InputStream` and `OutputStream`.
|
||||||
|
`HttpMessageConverter` instances are used on the client side (for example, in the `RestClient`) and on the server side (for example, in Spring MVC REST controllers).
|
||||||
|
|
||||||
|
Concrete implementations for the main media (MIME) types are provided in the framework and are, by default, registered with the `RestClient` and `RestTemplate` on the client side and with `RequestMappingHandlerAdapter` on the server side (see xref:web/webmvc/mvc-config/message-converters.adoc[Configuring Message Converters]).
|
||||||
|
|
||||||
|
Several implementations of `HttpMessageConverter` are described below.
|
||||||
|
Refer to the {api-spring-framework}/http/converter/HttpMessageConverter.html[`HttpMessageConverter` Javadoc] for the complete list.
|
||||||
|
For all converters, a default media type is used, but you can override it by setting the `supportedMediaTypes` property.
|
||||||
|
|
||||||
|
[[rest-message-converters-tbl]]
|
||||||
|
.HttpMessageConverter Implementations
|
||||||
|
[cols="1,3"]
|
||||||
|
|===
|
||||||
|
| MessageConverter | Description
|
||||||
|
|
||||||
|
| `StringHttpMessageConverter`
|
||||||
|
| An `HttpMessageConverter` implementation that can read and write `String` instances from the HTTP request and response.
|
||||||
|
By default, this converter supports all text media types(`text/{asterisk}`) and writes with a `Content-Type` of `text/plain`.
|
||||||
|
|
||||||
|
| `FormHttpMessageConverter`
|
||||||
|
| An `HttpMessageConverter` implementation that can read and write form data from the HTTP request and response.
|
||||||
|
By default, this converter reads and writes the `application/x-www-form-urlencoded` media type.
|
||||||
|
Form data is read from and written into a `MultiValueMap<String, String>`.
|
||||||
|
The converter can also write (but not read) multipart data read from a `MultiValueMap<String, Object>`.
|
||||||
|
By default, `multipart/form-data` is supported.
|
||||||
|
Additional multipart subtypes can be supported for writing form data.
|
||||||
|
Consult the javadoc for `FormHttpMessageConverter` for further details.
|
||||||
|
|
||||||
|
| `ByteArrayHttpMessageConverter`
|
||||||
|
| An `HttpMessageConverter` implementation that can read and write byte arrays from the HTTP request and response.
|
||||||
|
By default, this converter supports all media types (`{asterisk}/{asterisk}`) and writes with a `Content-Type` of `application/octet-stream`.
|
||||||
|
You can override this by setting the `supportedMediaTypes` property and overriding `getContentType(byte[])`.
|
||||||
|
|
||||||
|
| `MarshallingHttpMessageConverter`
|
||||||
|
| An `HttpMessageConverter` implementation that can read and write XML by using Spring's `Marshaller` and `Unmarshaller` abstractions from the `org.springframework.oxm` package.
|
||||||
|
This converter requires a `Marshaller` and `Unmarshaller` before it can be used.
|
||||||
|
You can inject these through constructor or bean properties.
|
||||||
|
By default, this converter supports `text/xml` and `application/xml`.
|
||||||
|
|
||||||
|
| `MappingJackson2HttpMessageConverter`
|
||||||
|
| An `HttpMessageConverter` implementation that can read and write JSON by using Jackson's `ObjectMapper`.
|
||||||
|
You can customize JSON mapping as needed through the use of Jackson's provided annotations.
|
||||||
|
When you need further control (for cases where custom JSON serializers/deserializers need to be provided for specific types), you can inject a custom `ObjectMapper` through the `ObjectMapper` property.
|
||||||
|
By default, this converter supports `application/json`.
|
||||||
|
|
||||||
|
| `MappingJackson2XmlHttpMessageConverter`
|
||||||
|
| An `HttpMessageConverter` implementation that can read and write XML by using https://github.com/FasterXML/jackson-dataformat-xml[Jackson XML] extension's `XmlMapper`.
|
||||||
|
You can customize XML mapping as needed through the use of JAXB or Jackson's provided annotations.
|
||||||
|
When you need further control (for cases where custom XML serializers/deserializers need to be provided for specific types), you can inject a custom `XmlMapper` through the `ObjectMapper` property.
|
||||||
|
By default, this converter supports `application/xml`.
|
||||||
|
|
||||||
|
| `SourceHttpMessageConverter`
|
||||||
|
| An `HttpMessageConverter` implementation that can read and write `javax.xml.transform.Source` from the HTTP request and response.
|
||||||
|
Only `DOMSource`, `SAXSource`, and `StreamSource` are supported.
|
||||||
|
By default, this converter supports `text/xml` and `application/xml`.
|
||||||
|
|
||||||
|
|===
|
||||||
|
|
||||||
|
By default, `RestClient` and `RestTemplate` register all built-in message converters, depending on the availability of underlying libraries on the classpath.
|
||||||
|
You can also set the message converters to use explicitly, by using `messageConverters` on the `RestClient` builder, or via the `messageConverters` property of `RestTemplate`.
|
||||||
|
|
||||||
|
==== Jackson JSON Views
|
||||||
|
|
||||||
|
To serialize only a subset of the object properties, you can specify a https://www.baeldung.com/jackson-json-view-annotation[Jackson JSON View], as the following example shows:
|
||||||
|
|
||||||
|
[source,java,indent=0,subs="verbatim"]
|
||||||
|
----
|
||||||
|
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:
|
||||||
|
|
||||||
|
[source,java,indent=0,subs="verbatim"]
|
||||||
|
----
|
||||||
|
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.
|
||||||
|
|
||||||
|
[[rest-request-factories]]
|
||||||
|
=== 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's `HttpClient`,
|
||||||
|
* `HttpComponentsClientHttpRequestFactory` for use with Apache HTTP Components `HttpClient`,
|
||||||
|
* `JettyClientHttpRequestFactory` for Jetty's `HttpClient`,
|
||||||
|
* `ReactorNettyClientRequestFactory` for Reactor Netty's `HttpClient`,
|
||||||
|
* `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.
|
||||||
|
|
||||||
[[rest-webclient]]
|
[[rest-webclient]]
|
||||||
== `WebClient`
|
== `WebClient`
|
||||||
|
|
@ -40,9 +488,8 @@ See xref:web/webflux-webclient.adoc[WebClient] for more details.
|
||||||
[[rest-resttemplate]]
|
[[rest-resttemplate]]
|
||||||
== `RestTemplate`
|
== `RestTemplate`
|
||||||
|
|
||||||
The `RestTemplate` provides a higher level API over HTTP client libraries. It makes it
|
The `RestTemplate` provides a high-level API over HTTP client libraries in the form of a classic Spring Template class.
|
||||||
easy to invoke REST endpoints in a single line. It exposes the following groups of
|
It exposes the following groups of overloaded methods:
|
||||||
overloaded methods:
|
|
||||||
|
|
||||||
NOTE: The xref:integration/rest-clients.adoc#rest-restclient[`RestClient`] offers a more modern API for synchronous HTTP access.
|
NOTE: The xref:integration/rest-clients.adoc#rest-restclient[`RestClient`] offers a more modern API for synchronous HTTP access.
|
||||||
For asynchronous and streaming scenarios, consider the reactive xref:web/webflux-webclient.adoc[WebClient].
|
For asynchronous and streaming scenarios, consider the reactive xref:web/webflux-webclient.adoc[WebClient].
|
||||||
|
|
@ -76,8 +523,7 @@ For asynchronous and streaming scenarios, consider the reactive xref:web/webflux
|
||||||
|
|
||||||
| `patchForObject`
|
| `patchForObject`
|
||||||
| Updates a resource by using PATCH and returns the representation from the response.
|
| Updates a resource by using PATCH and returns the representation from the response.
|
||||||
Note that the JDK `HttpURLConnection` does not support `PATCH`, but Apache
|
Note that the JDK `HttpURLConnection` does not support `PATCH`, but Apache HttpComponents and others do.
|
||||||
HttpComponents and others do.
|
|
||||||
|
|
||||||
| `delete`
|
| `delete`
|
||||||
| Deletes the resources at the specified URI by using DELETE.
|
| Deletes the resources at the specified URI by using DELETE.
|
||||||
|
|
@ -86,9 +532,8 @@ HttpComponents and others do.
|
||||||
| Retrieves allowed HTTP methods for a resource by using ALLOW.
|
| Retrieves allowed HTTP methods for a resource by using ALLOW.
|
||||||
|
|
||||||
| `exchange`
|
| `exchange`
|
||||||
| More generalized (and less opinionated) version of the preceding methods that provides extra
|
| More generalized (and less opinionated) version of the preceding methods that provides extra flexibility when needed.
|
||||||
flexibility when needed. It accepts a `RequestEntity` (including HTTP method, URL, headers,
|
It accepts a `RequestEntity` (including HTTP method, URL, headers, and body as input) and returns a `ResponseEntity`.
|
||||||
and body as input) and returns a `ResponseEntity`.
|
|
||||||
|
|
||||||
These methods allow the use of `ParameterizedTypeReference` instead of `Class` to specify
|
These methods allow the use of `ParameterizedTypeReference` instead of `Class` to specify
|
||||||
a response type with generics.
|
a response type with generics.
|
||||||
|
|
@ -99,266 +544,19 @@ preparation and response extraction through callback interfaces.
|
||||||
|
|
||||||
|===
|
|===
|
||||||
|
|
||||||
[[rest-resttemplate-create]]
|
|
||||||
=== Initialization
|
=== Initialization
|
||||||
|
|
||||||
The default constructor uses `java.net.HttpURLConnection` to perform requests. You can
|
`RestTemplate` uses the same HTTP library abstraction as `RestClient`.
|
||||||
switch to a different HTTP library with an implementation of `ClientHttpRequestFactory`.
|
By default, it uses the `SimpleClientHttpRequestFactory`, but this can be changed via the constructor.
|
||||||
There is built-in support for the following:
|
See <<rest-request-factories>>.
|
||||||
|
|
||||||
* Apache HttpComponents
|
|
||||||
* Netty
|
|
||||||
* OkHttp
|
|
||||||
|
|
||||||
For example, to switch to Apache HttpComponents, you can use the following:
|
|
||||||
|
|
||||||
[source,java,indent=0,subs="verbatim,quotes"]
|
|
||||||
----
|
|
||||||
RestTemplate template = new RestTemplate(new HttpComponentsClientHttpRequestFactory());
|
|
||||||
----
|
|
||||||
|
|
||||||
Each `ClientHttpRequestFactory` exposes configuration options specific to the underlying
|
|
||||||
HTTP client library -- for example, for credentials, connection pooling, and other details.
|
|
||||||
|
|
||||||
TIP: Note that the `java.net` implementation for HTTP requests can raise an exception when
|
|
||||||
accessing the status of a response that represents an error (such as 401). If this is an
|
|
||||||
issue, switch to another HTTP client library.
|
|
||||||
|
|
||||||
NOTE: `RestTemplate` can be instrumented for observability, in order to produce metrics and traces.
|
NOTE: `RestTemplate` can be instrumented for observability, in order to produce metrics and traces.
|
||||||
See the xref:integration/observability.adoc#http-client.resttemplate[RestTemplate Observability support] section.
|
See the xref:integration/observability.adoc#http-client.resttemplate[RestTemplate Observability support] section.
|
||||||
|
|
||||||
[[rest-resttemplate-uri]]
|
|
||||||
==== URIs
|
|
||||||
|
|
||||||
Many of the `RestTemplate` methods accept a URI template and URI template variables,
|
|
||||||
either as a `String` variable argument, or as `Map<String,String>`.
|
|
||||||
|
|
||||||
The following example uses a `String` variable argument:
|
|
||||||
|
|
||||||
[source,java,indent=0,subs="verbatim,quotes"]
|
|
||||||
----
|
|
||||||
String result = restTemplate.getForObject(
|
|
||||||
"https://example.com/hotels/{hotel}/bookings/{booking}", String.class, "42", "21");
|
|
||||||
----
|
|
||||||
|
|
||||||
The following example uses a `Map<String, String>`:
|
|
||||||
|
|
||||||
[source,java,indent=0,subs="verbatim,quotes"]
|
|
||||||
----
|
|
||||||
Map<String, String> vars = Collections.singletonMap("hotel", "42");
|
|
||||||
|
|
||||||
String result = restTemplate.getForObject(
|
|
||||||
"https://example.com/hotels/{hotel}/rooms/{hotel}", String.class, vars);
|
|
||||||
----
|
|
||||||
|
|
||||||
Keep in mind URI templates are automatically encoded, as the following example shows:
|
|
||||||
|
|
||||||
[source,java,indent=0,subs="verbatim,quotes"]
|
|
||||||
----
|
|
||||||
restTemplate.getForObject("https://example.com/hotel list", String.class);
|
|
||||||
|
|
||||||
// Results in request to "https://example.com/hotel%20list"
|
|
||||||
----
|
|
||||||
|
|
||||||
You can use the `uriTemplateHandler` property of `RestTemplate` to customize how URIs
|
|
||||||
are encoded. Alternatively, you can prepare a `java.net.URI` and pass it into one of
|
|
||||||
the `RestTemplate` methods that accepts a `URI`.
|
|
||||||
|
|
||||||
For more details on working with and encoding URIs, see xref:web/webmvc/mvc-uri-building.adoc[URI Links].
|
|
||||||
|
|
||||||
[[rest-template-headers]]
|
|
||||||
==== Headers
|
|
||||||
|
|
||||||
You can use the `exchange()` methods to specify request headers, as the following example shows:
|
|
||||||
|
|
||||||
[source,java,indent=0,subs="verbatim,quotes"]
|
|
||||||
----
|
|
||||||
String uriTemplate = "https://example.com/hotels/{hotel}";
|
|
||||||
URI uri = UriComponentsBuilder.fromUriString(uriTemplate).build(42);
|
|
||||||
|
|
||||||
RequestEntity<Void> requestEntity = RequestEntity.get(uri)
|
|
||||||
.header("MyRequestHeader", "MyValue")
|
|
||||||
.build();
|
|
||||||
|
|
||||||
ResponseEntity<String> response = template.exchange(requestEntity, String.class);
|
|
||||||
|
|
||||||
String responseHeader = response.getHeaders().getFirst("MyResponseHeader");
|
|
||||||
String body = response.getBody();
|
|
||||||
----
|
|
||||||
|
|
||||||
You can obtain response headers through many `RestTemplate` method variants that return
|
|
||||||
`ResponseEntity`.
|
|
||||||
|
|
||||||
[[rest-template-body]]
|
[[rest-template-body]]
|
||||||
=== Body
|
=== Body
|
||||||
|
|
||||||
Objects passed into and returned from `RestTemplate` methods are converted to and from raw
|
Objects passed into and returned from `RestTemplate` methods are converted to and from HTTP messages with the help of an `HttpMessageConverter`, see <<rest-message-conversion>>.
|
||||||
content with the help of an `HttpMessageConverter`.
|
|
||||||
|
|
||||||
On a POST, an input object is serialized to the request body, as the following example shows:
|
|
||||||
|
|
||||||
----
|
|
||||||
URI location = template.postForLocation("https://example.com/people", person);
|
|
||||||
----
|
|
||||||
|
|
||||||
You need not explicitly set the Content-Type header of the request. In most cases,
|
|
||||||
you can find a compatible message converter based on the source `Object` type, and the chosen
|
|
||||||
message converter sets the content type accordingly. If necessary, you can use the
|
|
||||||
`exchange` methods to explicitly provide the `Content-Type` request header, and that, in
|
|
||||||
turn, influences what message converter is selected.
|
|
||||||
|
|
||||||
On a GET, the body of the response is deserialized to an output `Object`, as the following example shows:
|
|
||||||
|
|
||||||
----
|
|
||||||
Person person = restTemplate.getForObject("https://example.com/people/{id}", Person.class, 42);
|
|
||||||
----
|
|
||||||
|
|
||||||
The `Accept` header of the request does not need to be explicitly set. In most cases,
|
|
||||||
a compatible message converter can be found based on the expected response type, which
|
|
||||||
then helps to populate the `Accept` header. If necessary, you can use the `exchange`
|
|
||||||
methods to provide the `Accept` header explicitly.
|
|
||||||
|
|
||||||
By default, `RestTemplate` registers all built-in
|
|
||||||
xref:integration/rest-clients.adoc#rest-message-conversion[message converters], depending on classpath checks that help
|
|
||||||
to determine what optional conversion libraries are present. You can also set the message
|
|
||||||
converters to use explicitly.
|
|
||||||
|
|
||||||
[[rest-message-conversion]]
|
|
||||||
==== Message Conversion
|
|
||||||
[.small]#xref:web/webflux/reactive-spring.adoc#webflux-codecs[See equivalent in the Reactive stack]#
|
|
||||||
|
|
||||||
The `spring-web` module contains the `HttpMessageConverter` contract for reading and
|
|
||||||
writing the body of HTTP requests and responses through `InputStream` and `OutputStream`.
|
|
||||||
`HttpMessageConverter` instances are used on the client side (for example, in the `RestTemplate`) and
|
|
||||||
on the server side (for example, in Spring MVC REST controllers).
|
|
||||||
|
|
||||||
Concrete implementations for the main media (MIME) types are provided in the framework
|
|
||||||
and are, by default, registered with the `RestTemplate` on the client side and with
|
|
||||||
`RequestMappingHandlerAdapter` on the server side (see
|
|
||||||
xref:web/webmvc/mvc-config/message-converters.adoc[Configuring Message Converters]).
|
|
||||||
|
|
||||||
The implementations of `HttpMessageConverter` are described in the following sections.
|
|
||||||
For all converters, a default media type is used, but you can override it by setting the
|
|
||||||
`supportedMediaTypes` bean property. The following table describes each implementation:
|
|
||||||
|
|
||||||
[[rest-message-converters-tbl]]
|
|
||||||
.HttpMessageConverter Implementations
|
|
||||||
[cols="1,3"]
|
|
||||||
|===
|
|
||||||
| MessageConverter | Description
|
|
||||||
|
|
||||||
| `StringHttpMessageConverter`
|
|
||||||
| An `HttpMessageConverter` implementation that can read and write `String` instances from the HTTP
|
|
||||||
request and response. By default, this converter supports all text media types
|
|
||||||
(`text/{asterisk}`) and writes with a `Content-Type` of `text/plain`.
|
|
||||||
|
|
||||||
| `FormHttpMessageConverter`
|
|
||||||
| An `HttpMessageConverter` implementation that can read and write form data from the HTTP
|
|
||||||
request and response. By default, this converter reads and writes the
|
|
||||||
`application/x-www-form-urlencoded` media type. Form data is read from and written into a
|
|
||||||
`MultiValueMap<String, String>`. The converter can also write (but not read) multipart
|
|
||||||
data read from a `MultiValueMap<String, Object>`. By default, `multipart/form-data` is
|
|
||||||
supported. As of Spring Framework 5.2, additional multipart subtypes can be supported for
|
|
||||||
writing form data. Consult the javadoc for `FormHttpMessageConverter` for further details.
|
|
||||||
|
|
||||||
| `ByteArrayHttpMessageConverter`
|
|
||||||
| An `HttpMessageConverter` implementation that can read and write byte arrays from the
|
|
||||||
HTTP request and response. By default, this converter supports all media types (`{asterisk}/{asterisk}`)
|
|
||||||
and writes with a `Content-Type` of `application/octet-stream`. You can override this
|
|
||||||
by setting the `supportedMediaTypes` property and overriding `getContentType(byte[])`.
|
|
||||||
|
|
||||||
| `MarshallingHttpMessageConverter`
|
|
||||||
| An `HttpMessageConverter` implementation that can read and write XML by using Spring's
|
|
||||||
`Marshaller` and `Unmarshaller` abstractions from the `org.springframework.oxm` package.
|
|
||||||
This converter requires a `Marshaller` and `Unmarshaller` before it can be used. You can inject these
|
|
||||||
through constructor or bean properties. By default, this converter supports
|
|
||||||
`text/xml` and `application/xml`.
|
|
||||||
|
|
||||||
| `MappingJackson2HttpMessageConverter`
|
|
||||||
| An `HttpMessageConverter` implementation that can read and write JSON by using Jackson's
|
|
||||||
`ObjectMapper`. You can customize JSON mapping as needed through the use of Jackson's
|
|
||||||
provided annotations. When you need further control (for cases where custom JSON
|
|
||||||
serializers/deserializers need to be provided for specific types), you can inject a custom `ObjectMapper`
|
|
||||||
through the `ObjectMapper` property. By default, this
|
|
||||||
converter supports `application/json`.
|
|
||||||
|
|
||||||
| `MappingJackson2XmlHttpMessageConverter`
|
|
||||||
| An `HttpMessageConverter` implementation that can read and write XML by using
|
|
||||||
https://github.com/FasterXML/jackson-dataformat-xml[Jackson XML] extension's
|
|
||||||
`XmlMapper`. You can customize XML mapping as needed through the use of JAXB
|
|
||||||
or Jackson's provided annotations. When you need further control (for cases where custom XML
|
|
||||||
serializers/deserializers need to be provided for specific types), you can inject a custom `XmlMapper`
|
|
||||||
through the `ObjectMapper` property. By default, this
|
|
||||||
converter supports `application/xml`.
|
|
||||||
|
|
||||||
| `SourceHttpMessageConverter`
|
|
||||||
| An `HttpMessageConverter` implementation that can read and write
|
|
||||||
`javax.xml.transform.Source` from the HTTP request and response. Only `DOMSource`,
|
|
||||||
`SAXSource`, and `StreamSource` are supported. By default, this converter supports
|
|
||||||
`text/xml` and `application/xml`.
|
|
||||||
|
|
||||||
| `BufferedImageHttpMessageConverter`
|
|
||||||
| An `HttpMessageConverter` implementation that can read and write
|
|
||||||
`java.awt.image.BufferedImage` from the HTTP request and response. This converter reads
|
|
||||||
and writes the media type supported by the Java I/O API.
|
|
||||||
|
|
||||||
|===
|
|
||||||
|
|
||||||
[[rest-template-jsonview]]
|
|
||||||
=== Jackson JSON Views
|
|
||||||
|
|
||||||
You can specify a https://www.baeldung.com/jackson-json-view-annotation[Jackson JSON View]
|
|
||||||
to serialize only a subset of the object properties, as the following example shows:
|
|
||||||
|
|
||||||
[source,java,indent=0,subs="verbatim,quotes"]
|
|
||||||
----
|
|
||||||
MappingJacksonValue value = new MappingJacksonValue(new User("eric", "7!jd#h23"));
|
|
||||||
value.setSerializationView(User.WithoutPasswordView.class);
|
|
||||||
|
|
||||||
RequestEntity<MappingJacksonValue> requestEntity =
|
|
||||||
RequestEntity.post(new URI("https://example.com/user")).body(value);
|
|
||||||
|
|
||||||
ResponseEntity<String> response = template.exchange(requestEntity, String.class);
|
|
||||||
----
|
|
||||||
|
|
||||||
[[rest-template-multipart]]
|
|
||||||
=== 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:
|
|
||||||
|
|
||||||
[source,java,indent=0,subs="verbatim,quotes"]
|
|
||||||
----
|
|
||||||
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));
|
|
||||||
----
|
|
||||||
|
|
||||||
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 pass it to the `RestTemplate`, as show below:
|
|
||||||
|
|
||||||
[source,java,indent=0,subs="verbatim,quotes"]
|
|
||||||
----
|
|
||||||
MultiValueMap<String, Object> parts = ...;
|
|
||||||
template.postForObject("https://example.com/upload", parts, Void.class);
|
|
||||||
----
|
|
||||||
|
|
||||||
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` is defaulted to `application/x-www-form-urlencoded`.
|
|
||||||
If necessary the `Content-Type` may also be set explicitly.
|
|
||||||
|
|
||||||
|
|
||||||
[[rest-http-interface]]
|
[[rest-http-interface]]
|
||||||
== HTTP Interface
|
== HTTP Interface
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue