521 lines
20 KiB
Plaintext
521 lines
20 KiB
Plaintext
[[rest-client-access]]
|
|
= REST Clients
|
|
|
|
The Spring Framework provides the following choices for making calls to REST endpoints:
|
|
|
|
* xref:integration/rest-clients.adoc#rest-webclient[`WebClient`] - non-blocking, reactive client w fluent API.
|
|
* xref:integration/rest-clients.adoc#rest-resttemplate[`RestTemplate`] - synchronous client with template method API.
|
|
* xref:integration/rest-clients.adoc#rest-http-interface[HTTP Interface] - annotated interface with generated, dynamic proxy implementation.
|
|
|
|
|
|
[[rest-webclient]]
|
|
== `WebClient`
|
|
|
|
`WebClient` is a non-blocking, reactive client to perform HTTP requests. It was
|
|
introduced in 5.0 and offers an alternative to the `RestTemplate`, with support for
|
|
synchronous, asynchronous, and streaming scenarios.
|
|
|
|
`WebClient` supports the following:
|
|
|
|
* Non-blocking I/O.
|
|
* Reactive Streams back pressure.
|
|
* High concurrency with fewer hardware resources.
|
|
* Functional-style, fluent API that takes advantage of Java 8 lambdas.
|
|
* Synchronous and asynchronous interactions.
|
|
* Streaming up to or streaming down from a server.
|
|
|
|
See xref:web/webflux-webclient.adoc[WebClient] for more details.
|
|
|
|
|
|
|
|
|
|
[[rest-resttemplate]]
|
|
== `RestTemplate`
|
|
|
|
The `RestTemplate` provides a higher level API over HTTP client libraries. It makes it
|
|
easy to invoke REST endpoints in a single line. It exposes the following groups of
|
|
overloaded methods:
|
|
|
|
NOTE: `RestTemplate` is in maintenance mode, with only requests for minor
|
|
changes and bugs to be accepted. Please, consider using the
|
|
xref:web/webflux-webclient.adoc[WebClient] instead.
|
|
|
|
[[rest-overview-of-resttemplate-methods-tbl]]
|
|
.RestTemplate methods
|
|
[cols="1,3"]
|
|
|===
|
|
| Method group | Description
|
|
|
|
| `getForObject`
|
|
| Retrieves a representation via GET.
|
|
|
|
| `getForEntity`
|
|
| Retrieves a `ResponseEntity` (that is, status, headers, and body) by using GET.
|
|
|
|
| `headForHeaders`
|
|
| Retrieves all headers for a resource by using HEAD.
|
|
|
|
| `postForLocation`
|
|
| Creates a new resource by using POST and returns the `Location` header from the response.
|
|
|
|
| `postForObject`
|
|
| Creates a new resource by using POST and returns the representation from the response.
|
|
|
|
| `postForEntity`
|
|
| Creates a new resource by using POST and returns the representation from the response.
|
|
|
|
| `put`
|
|
| Creates or updates a resource by using PUT.
|
|
|
|
| `patchForObject`
|
|
| Updates a resource by using PATCH and returns the representation from the response.
|
|
Note that the JDK `HttpURLConnection` does not support `PATCH`, but Apache
|
|
HttpComponents and others do.
|
|
|
|
| `delete`
|
|
| Deletes the resources at the specified URI by using DELETE.
|
|
|
|
| `optionsForAllow`
|
|
| Retrieves allowed HTTP methods for a resource by using ALLOW.
|
|
|
|
| `exchange`
|
|
| More generalized (and less opinionated) version of the preceding methods that provides extra
|
|
flexibility when needed. It accepts a `RequestEntity` (including HTTP method, URL, headers,
|
|
and body as input) and returns a `ResponseEntity`.
|
|
|
|
These methods allow the use of `ParameterizedTypeReference` instead of `Class` to specify
|
|
a response type with generics.
|
|
|
|
| `execute`
|
|
| The most generalized way to perform a request, with full control over request
|
|
preparation and response extraction through callback interfaces.
|
|
|
|
|===
|
|
|
|
[[rest-resttemplate-create]]
|
|
=== Initialization
|
|
|
|
The default constructor uses `java.net.HttpURLConnection` to perform requests. You can
|
|
switch to a different HTTP library with an implementation of `ClientHttpRequestFactory`.
|
|
There is built-in support for the following:
|
|
|
|
* 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.
|
|
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]]
|
|
=== Body
|
|
|
|
Objects passed into and returned from `RestTemplate` methods are converted to and from raw
|
|
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]]
|
|
== HTTP Interface
|
|
|
|
The Spring Framework lets you define an HTTP service as a Java interface with annotated
|
|
methods for HTTP exchanges. You can then generate a proxy that implements this interface
|
|
and performs the exchanges. This helps to simplify HTTP remote access which often
|
|
involves a facade that wraps the details of using the underlying HTTP client.
|
|
|
|
One, declare an interface with `@HttpExchange` methods:
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
interface RepositoryService {
|
|
|
|
@GetExchange("/repos/{owner}/{repo}")
|
|
Repository getRepository(@PathVariable String owner, @PathVariable String repo);
|
|
|
|
// more HTTP exchange methods...
|
|
|
|
}
|
|
----
|
|
|
|
Two, create a proxy that will perform the declared HTTP exchanges:
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
WebClient client = WebClient.builder().baseUrl("https://api.github.com/").build();
|
|
HttpServiceProxyFactory factory = HttpServiceProxyFactory.builder(WebClientAdapter.forClient(client)).build();
|
|
|
|
RepositoryService service = factory.createClient(RepositoryService.class);
|
|
----
|
|
|
|
`@HttpExchange` is supported at the type level where it applies to all methods:
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
@HttpExchange(url = "/repos/{owner}/{repo}", accept = "application/vnd.github.v3+json")
|
|
interface RepositoryService {
|
|
|
|
@GetExchange
|
|
Repository getRepository(@PathVariable String owner, @PathVariable String repo);
|
|
|
|
@PatchExchange(contentType = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
|
|
void updateRepository(@PathVariable String owner, @PathVariable String repo,
|
|
@RequestParam String name, @RequestParam String description, @RequestParam String homepage);
|
|
|
|
}
|
|
----
|
|
|
|
|
|
[[rest-http-interface-method-parameters]]
|
|
=== Method Parameters
|
|
|
|
Annotated, HTTP exchange methods support flexible method signatures with the following
|
|
method parameters:
|
|
|
|
[cols="1,2", options="header"]
|
|
|===
|
|
| Method argument | Description
|
|
|
|
| `URI`
|
|
| Dynamically set the URL for the request, overriding the annotation's `url` attribute.
|
|
|
|
| `HttpMethod`
|
|
| Dynamically set the HTTP method for the request, overriding the annotation's `method` attribute
|
|
|
|
| `@RequestHeader`
|
|
| Add a request header or multiple headers. The argument may be a `Map<String, ?>` or
|
|
`MultiValueMap<String, ?>` with multiple headers, a `Collection<?>` of values, or an
|
|
individual value. Type conversion is supported for non-String values.
|
|
|
|
| `@PathVariable`
|
|
| Add a variable for expand a placeholder in the request URL. The argument may be a
|
|
`Map<String, ?>` with multiple variables, or an individual value. Type conversion
|
|
is supported for non-String values.
|
|
|
|
| `@RequestBody`
|
|
| Provide the body of the request either as an Object to be serialized, or a
|
|
Reactive Streams `Publisher` such as `Mono`, `Flux`, or any other async type supported
|
|
through the configured `ReactiveAdapterRegistry`.
|
|
|
|
| `@RequestParam`
|
|
| Add a request parameter or multiple parameters. The argument may be a `Map<String, ?>`
|
|
or `MultiValueMap<String, ?>` with multiple parameters, a `Collection<?>` of values, or
|
|
an individual value. Type conversion is supported for non-String values.
|
|
|
|
When `"content-type"` is set to `"application/x-www-form-urlencoded"`, request
|
|
parameters are encoded in the request body. Otherwise, they are added as URL query
|
|
parameters.
|
|
|
|
| `@RequestPart`
|
|
| Add a request part, which may be a String (form field), `Resource` (file part),
|
|
Object (entity to be encoded, e.g. as JSON), `HttpEntity` (part content and headers),
|
|
a Spring `Part`, or Reactive Streams `Publisher` of any of the above.
|
|
|
|
| `@CookieValue`
|
|
| Add a cookie or multiple cookies. The argument may be a `Map<String, ?>` or
|
|
`MultiValueMap<String, ?>` with multiple cookies, a `Collection<?>` of values, or an
|
|
individual value. Type conversion is supported for non-String values.
|
|
|
|
|===
|
|
|
|
|
|
[[rest-http-interface-return-values]]
|
|
=== Return Values
|
|
|
|
Annotated, HTTP exchange methods support the following return values:
|
|
|
|
[cols="1,2", options="header"]
|
|
|===
|
|
| Method return value | Description
|
|
|
|
| `void`, `Mono<Void>`
|
|
| Perform the given request, and release the response content, if any.
|
|
|
|
| `HttpHeaders`, `Mono<HttpHeaders>`
|
|
| Perform the given request, release the response content, if any, and return the
|
|
response headers.
|
|
|
|
| `<T>`, `Mono<T>`
|
|
| Perform the given request and decode the response content to the declared return type.
|
|
|
|
| `<T>`, `Flux<T>`
|
|
| Perform the given request and decode the response content to a stream of the declared
|
|
element type.
|
|
|
|
| `ResponseEntity<Void>`, `Mono<ResponseEntity<Void>>`
|
|
| Perform the given request, and release the response content, if any, and return a
|
|
`ResponseEntity` with the status and headers.
|
|
|
|
| `ResponseEntity<T>`, `Mono<ResponseEntity<T>>`
|
|
| Perform the given request, decode the response content to the declared return type, and
|
|
return a `ResponseEntity` with the status, headers, and the decoded body.
|
|
|
|
| `Mono<ResponseEntity<Flux<T>>`
|
|
| Perform the given request, decode the response content to a stream of the declared
|
|
element type, and return a `ResponseEntity` with the status, headers, and the decoded
|
|
response body stream.
|
|
|
|
|===
|
|
|
|
TIP: You can also use any other async or reactive types registered in the
|
|
`ReactiveAdapterRegistry`.
|
|
|
|
|
|
[[rest-http-interface-exceptions]]
|
|
=== Exception Handling
|
|
|
|
By default, `WebClient` raises `WebClientResponseException` for 4xx and 5xx HTTP status
|
|
codes. To customize this, you can register a response status handler that applies to all
|
|
responses performed through the client:
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
WebClient webClient = WebClient.builder()
|
|
.defaultStatusHandler(HttpStatusCode::isError, resp -> ...)
|
|
.build();
|
|
|
|
WebClientAdapter clientAdapter = WebClientAdapter.forClient(webClient);
|
|
HttpServiceProxyFactory factory = HttpServiceProxyFactory
|
|
.builder(clientAdapter).build();
|
|
----
|
|
|
|
For more details and options, such as suppressing error status codes, see the Javadoc of
|
|
`defaultStatusHandler` in `WebClient.Builder`.
|