You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
520 lines
20 KiB
520 lines
20 KiB
[[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`.
|
|
|