[[rest-client-access]] = REST Clients The Spring Framework provides the following choices for making calls to REST endpoints: * xref:integration/rest-clients.adoc#rest-restclient[`RestClient`] - synchronous client with a fluent API. * xref:integration/rest-clients.adoc#rest-webclient[`WebClient`] - non-blocking, reactive client with 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-restclient]] == `RestClient` 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 (see <>) and which message converters to use (see <>), 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. [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(mapOf("variable" to "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 a `String`, with optional URI template variables. The following example configures a GET request to `https://example.com/orders/42`: [tabs] ====== Java:: + [source,java,indent=0,subs="verbatim,quotes",role="primary"] ---- int id = 42; restClient.get() .uri("https://example.com/orders/{id}", id) .... ---- Kotlin:: + [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] ---- val id = 42 restClient.get() .uri("https://example.com/orders/{id}", id) ... ---- ====== A function can also be used for more controls, such as specifying xref:web/webmvc/mvc-uri-building.adoc[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. 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`, 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 <>. 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 can be converted into objects using Jackson, and so on (see <>). 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() <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 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() <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() <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 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 overridden 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() ---- <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 use `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 xref:web/webmvc/message-converters.adoc#message-converters[See the supported HTTP message converters in the dedicated section]. ==== Jackson JSON Views To serialize only a subset of the object properties, you can specify a {baeldung-blog}/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 response = restClient.post() // or RestTemplate.postForEntity .contentType(APPLICATION_JSON) .body(value) .retrieve() .toBodilessEntity(); ---- ==== Multipart To send multipart data, you need to provide a `MultiValueMap` 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 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. TIP: Note that the `SimpleClientHttpRequestFactory` may raise an exception when accessing the status of a response that represents an error (e.g. 401). If this is an issue, use any of the alternative request factories. [[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 high-level API over HTTP client libraries in the form of a classic Spring Template class. It exposes the following groups of overloaded methods: 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]. [[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. |=== === Initialization `RestTemplate` uses the same HTTP library abstraction as `RestClient`. By default, it uses the `SimpleClientHttpRequestFactory`, but this can be changed via the constructor. See <>. 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-template-body]] === Body Objects passed into and returned from `RestTemplate` methods are converted to and from HTTP messages with the help of an `HttpMessageConverter`, see <>. === Migrating from `RestTemplate` to `RestClient` The following table shows `RestClient` equivalents for `RestTemplate` methods. It can be used to migrate from the latter to the former. .RestClient equivalents for RestTemplate methods [cols="1,1", options="header"] |=== | `RestTemplate` method | `RestClient` equivalent | `getForObject(String, Class, Object...)` | `get() .uri(String, Object...) .retrieve() .body(Class)` | `getForObject(String, Class, Map)` | `get() .uri(String, Map) .retrieve() .body(Class)` | `getForObject(URI, Class)` | `get() .uri(URI) .retrieve() .body(Class)` | `getForEntity(String, Class, Object...)` | `get() .uri(String, Object...) .retrieve() .toEntity(Class)` | `getForEntity(String, Class, Map)` | `get() .uri(String, Map) .retrieve() .toEntity(Class)` | `getForEntity(URI, Class)` | `get() .uri(URI) .retrieve() .toEntity(Class)` | `headForHeaders(String, Object...)` | `head() .uri(String, Object...) .retrieve() .toBodilessEntity() .getHeaders()` | `headForHeaders(String, Map)` | `head() .uri(String, Map) .retrieve() .toBodilessEntity() .getHeaders()` | `headForHeaders(URI)` | `head() .uri(URI) .retrieve() .toBodilessEntity() .getHeaders()` | `postForLocation(String, Object, Object...)` | `post() .uri(String, Object...) .body(Object).retrieve() .toBodilessEntity() .getLocation()` | `postForLocation(String, Object, Map)` | `post() .uri(String, Map) .body(Object) .retrieve() .toBodilessEntity() .getLocation()` | `postForLocation(URI, Object)` | `post() .uri(URI) .body(Object) .retrieve() .toBodilessEntity() .getLocation()` | `postForObject(String, Object, Class, Object...)` | `post() .uri(String, Object...) .body(Object) .retrieve() .body(Class)` | `postForObject(String, Object, Class, Map)` | `post() .uri(String, Map) .body(Object) .retrieve() .body(Class)` | `postForObject(URI, Object, Class)` | `post() .uri(URI) .body(Object) .retrieve() .body(Class)` | `postForEntity(String, Object, Class, Object...)` | `post() .uri(String, Object...) .body(Object) .retrieve() .toEntity(Class)` | `postForEntity(String, Object, Class, Map)` | `post() .uri(String, Map) .body(Object) .retrieve() .toEntity(Class)` | `postForEntity(URI, Object, Class)` | `post() .uri(URI) .body(Object) .retrieve() .toEntity(Class)` | `put(String, Object, Object...)` | `put() .uri(String, Object...) .body(Object) .retrieve() .toBodilessEntity()` | `put(String, Object, Map)` | `put() .uri(String, Map) .body(Object) .retrieve() .toBodilessEntity()` | `put(URI, Object)` | `put() .uri(URI) .body(Object) .retrieve() .toBodilessEntity()` | `patchForObject(String, Object, Class, Object...)` | `patch() .uri(String, Object...) .body(Object) .retrieve() .body(Class)` | `patchForObject(String, Object, Class, Map)` | `patch() .uri(String, Map) .body(Object) .retrieve() .body(Class)` | `patchForObject(URI, Object, Class)` | `patch() .uri(URI) .body(Object) .retrieve() .body(Class)` | `delete(String, Object...)` | `delete() .uri(String, Object...) .retrieve() .toBodilessEntity()` | `delete(String, Map)` | `delete() .uri(String, Map) .retrieve() .toBodilessEntity()` | `delete(URI)` | `delete() .uri(URI) .retrieve() .toBodilessEntity()` | `optionsForAllow(String, Object...)` | `options() .uri(String, Object...) .retrieve() .toBodilessEntity() .getAllow()` | `optionsForAllow(String, Map)` | `options() .uri(String, Map) .retrieve() .toBodilessEntity() .getAllow()` | `optionsForAllow(URI)` | `options() .uri(URI) .retrieve() .toBodilessEntity() .getAllow()` | `exchange(String, HttpMethod, HttpEntity, Class, Object...)` | `method(HttpMethod) .uri(String, Object...) .headers(Consumer) .body(Object) .retrieve() .toEntity(Class)` footnote:http-entity[`HttpEntity` headers and body have to be supplied to the `RestClient` via `headers(Consumer)` and `body(Object)`.] | `exchange(String, HttpMethod, HttpEntity, Class, Map)` | `method(HttpMethod) .uri(String, Map) .headers(Consumer) .body(Object) .retrieve() .toEntity(Class)` footnote:http-entity[] | `exchange(URI, HttpMethod, HttpEntity, Class)` | `method(HttpMethod) .uri(URI) .headers(Consumer) .body(Object) .retrieve() .toEntity(Class)` footnote:http-entity[] | `exchange(String, HttpMethod, HttpEntity, ParameterizedTypeReference, Object...)` | `method(HttpMethod) .uri(String, Object...) .headers(Consumer) .body(Object) .retrieve() .toEntity(ParameterizedTypeReference)` footnote:http-entity[] | `exchange(String, HttpMethod, HttpEntity, ParameterizedTypeReference, Map)` | `method(HttpMethod) .uri(String, Map) .headers(Consumer) .body(Object) .retrieve() .toEntity(ParameterizedTypeReference)` footnote:http-entity[] | `exchange(URI, HttpMethod, HttpEntity, ParameterizedTypeReference)` | `method(HttpMethod) .uri(URI) .headers(Consumer) .body(Object) .retrieve() .toEntity(ParameterizedTypeReference)` footnote:http-entity[] | `exchange(RequestEntity, Class)` | `method(HttpMethod) .uri(URI) .headers(Consumer) .body(Object) .retrieve() .toEntity(Class)` footnote:request-entity[`RequestEntity` method, URI, headers and body have to be supplied to the `RestClient` via `method(HttpMethod)`, `uri(URI)`, `headers(Consumer)` and `body(Object)`.] | `exchange(RequestEntity, ParameterizedTypeReference)` | `method(HttpMethod) .uri(URI) .headers(Consumer) .body(Object) .retrieve() .toEntity(ParameterizedTypeReference)` footnote:request-entity[] | `execute(String, HttpMethod, RequestCallback, ResponseExtractor, Object...)` | `method(HttpMethod) .uri(String, Object...) .exchange(ExchangeFunction)` | `execute(String, HttpMethod, RequestCallback, ResponseExtractor, Map)` | `method(HttpMethod) .uri(String, Map) .exchange(ExchangeFunction)` | `execute(URI, HttpMethod, RequestCallback, ResponseExtractor)` | `method(HttpMethod) .uri(URI) .exchange(ExchangeFunction)` |=== [[rest-http-interface]] == HTTP Interface The Spring Framework lets you define an HTTP service as a Java interface with `@HttpExchange` methods. You can pass such an interface to `HttpServiceProxyFactory` to create a proxy which performs requests through an HTTP client such as `RestClient` or `WebClient`. You can also implement the interface from an `@Controller` for server request handling. Start by creating the 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... } ---- Now you can create a proxy that performs requests when methods are called. For `RestClient`: [source,java,indent=0,subs="verbatim,quotes"] ---- RestClient restClient = RestClient.builder().baseUrl("https://api.github.com/").build(); RestClientAdapter adapter = RestClientAdapter.create(restClient); HttpServiceProxyFactory factory = HttpServiceProxyFactory.builderFor(adapter).build(); RepositoryService service = factory.createClient(RepositoryService.class); ---- For `WebClient`: [source,java,indent=0,subs="verbatim,quotes"] ---- WebClient webClient = WebClient.builder().baseUrl("https://api.github.com/").build(); WebClientAdapter adapter = WebClientAdapter.create(webClient); HttpServiceProxyFactory factory = HttpServiceProxyFactory.builderFor(adapter).build(); RepositoryService service = factory.createClient(RepositoryService.class); ---- For `RestTemplate`: [source,java,indent=0,subs="verbatim,quotes"] ---- RestTemplate restTemplate = new RestTemplate(); restTemplate.setUriTemplateHandler(new DefaultUriBuilderFactory("https://api.github.com/")); RestTemplateAdapter adapter = RestTemplateAdapter.create(restTemplate); HttpServiceProxyFactory factory = HttpServiceProxyFactory.builderFor(adapter).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. | `UriBuilderFactory` | Provide a `UriBuilderFactory` to expand the URI template and URI variables with. In effect, replaces the `UriBuilderFactory` (and its base URL) of the underlying client. | `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` or `MultiValueMap` with multiple headers, a `Collection` of values, or an individual value. Type conversion is supported for non-String values. This overrides the annotation's `headers` attribute. | `@PathVariable` | Add a variable for expand a placeholder in the request URL. The argument may be a `Map` with multiple variables, or an individual value. Type conversion is supported for non-String values. | `@RequestAttribute` | Provide an `Object` to add as a request attribute. Only supported by `RestClient` and `WebClient`. | `@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` or `MultiValueMap` 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. | `MultipartFile` | Add a request part from a `MultipartFile`, typically used in a Spring MVC controller where it represents an uploaded file. | `@CookieValue` | Add a cookie or multiple cookies. The argument may be a `Map` or `MultiValueMap` 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 The supported return values depend on the underlying client. Clients adapted to `HttpExchangeAdapter` such as `RestClient` and `RestTemplate` support synchronous return values: [cols="1,2", options="header"] |=== | Method return value | Description | `void` | Perform the given request. | `HttpHeaders` | Perform the given request and return the response headers. | `` | Perform the given request and decode the response content to the declared return type. | `ResponseEntity` | Perform the given request and return a `ResponseEntity` with the status and headers. | `ResponseEntity` | 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. |=== Clients adapted to `ReactorHttpExchangeAdapter` such as `WebClient`, support all of above as well as reactive variants. The table below shows Reactor types, but you can also use other reactive types that are supported through the `ReactiveAdapterRegistry`: [cols="1,2", options="header"] |=== | Method return value | Description | `Mono` | Perform the given request, and release the response content, if any. | `Mono` | Perform the given request, release the response content, if any, and return the response headers. | `Mono` | Perform the given request and decode the response content to the declared return type. | `Flux` | Perform the given request and decode the response content to a stream of the declared element type. | `Mono>` | Perform the given request, and release the response content, if any, and return a `ResponseEntity` with the status and headers. | `Mono>` | 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>` | 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. |=== By default, the timeout for synchronous return values with `ReactorHttpExchangeAdapter` depends on how the underlying HTTP client is configured. You can set a `blockTimeout` value on the adapter level as well, but we recommend relying on timeout settings of the underlying HTTP client, which operates at a lower level and provides more control. [[rest-http-interface-exceptions]] === Error Handling To customize error response handling, you need to configure the underlying HTTP client. For `RestClient`: By default, `RestClient` raises `RestClientException` for 4xx and 5xx HTTP status codes. To customize this, register a response status handler that applies to all responses performed through the client: [source,java,indent=0,subs="verbatim,quotes"] ---- RestClient restClient = RestClient.builder() .defaultStatusHandler(HttpStatusCode::isError, (request, response) -> ...) .build(); RestClientAdapter adapter = RestClientAdapter.create(restClient); HttpServiceProxyFactory factory = HttpServiceProxyFactory.builderFor(adapter).build(); ---- For more details and options, such as suppressing error status codes, see the Javadoc of `defaultStatusHandler` in `RestClient.Builder`. For `WebClient`: By default, `WebClient` raises `WebClientResponseException` for 4xx and 5xx HTTP status codes. To customize this, 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 adapter = WebClientAdapter.create(webClient); HttpServiceProxyFactory factory = HttpServiceProxyFactory.builder(adapter).build(); ---- For more details and options, such as suppressing error status codes, see the Javadoc of `defaultStatusHandler` in `WebClient.Builder`. For `RestTemplate`: By default, `RestTemplate` raises `RestClientException` for 4xx and 5xx HTTP status codes. To customize this, register an error handler that applies to all responses performed through the client: [source,java,indent=0,subs="verbatim,quotes"] ---- RestTemplate restTemplate = new RestTemplate(); restTemplate.setErrorHandler(myErrorHandler); RestTemplateAdapter adapter = RestTemplateAdapter.create(restTemplate); HttpServiceProxyFactory factory = HttpServiceProxyFactory.builderFor(adapter).build(); ---- For more details and options, see the Javadoc of `setErrorHandler` in `RestTemplate` and the `ResponseErrorHandler` hierarchy.