Polishing reference docs for HTTP Service clients

See gh-34912
This commit is contained in:
rstoyanchev 2025-05-30 15:33:06 +01:00
parent 05d59271d2
commit 81626b0734
1 changed files with 70 additions and 99 deletions

View File

@ -3,10 +3,10 @@
The Spring Framework provides the following choices for making calls to REST endpoints: 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-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-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-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. * xref:integration/rest-clients.adoc#rest-http-interface[HTTP Interface Clients] -- annotated interface backed by generated proxy
[[rest-restclient]] [[rest-restclient]]
@ -857,15 +857,17 @@ It can be used to migrate from the latter to the former.
[[rest-http-interface]] [[rest-http-interface]]
== HTTP Interface == HTTP Interface Clients
The Spring Framework lets you define an HTTP service as a Java interface with You can define an HTTP Service as a Java interface with `@HttpExchange` methods, and use
`@HttpExchange` methods. You can pass such an interface to `HttpServiceProxyFactory` `HttpServiceProxyFactory` to create a client proxy from it for remote access over HTTP via
to create a proxy which performs requests through an HTTP client such as `RestClient` `RestClient`, `WebClient`, or `RestTemplate`. On the server side, an `@Controller` class
or `WebClient`. You can also implement the interface from an `@Controller` for server can implement the same interface to handle requests with
request handling. xref:web/webmvc/mvc-controller/ann-requestmapping.adoc#mvc-ann-httpexchange-annotation[@HttpExchange]
controller methods.
Start by creating the interface with `@HttpExchange` methods:
First, create the Java interface:
[source,java,indent=0,subs="verbatim,quotes"] [source,java,indent=0,subs="verbatim,quotes"]
---- ----
@ -879,43 +881,7 @@ Start by creating the interface with `@HttpExchange` methods:
} }
---- ----
Now you can create a proxy that performs requests when methods are called. Optionally, use `@HttpExchange` at the type level to declare common attributes for all methods:
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"] [source,java,indent=0,subs="verbatim,quotes"]
---- ----
@ -933,15 +899,46 @@ For `RestTemplate`:
---- ----
Next, configure the client and create the `HttpServiceProxyFactory`:
[source,java,indent=0,subs="verbatim,quotes"]
----
// Using RestClient...
RestClient restClient = RestClient.create("...");
RestClientAdapter adapter = RestClientAdapter.create(restClient);
// or WebClient...
WebClient webClient = WebClient.create("...");
WebClientAdapter adapter = WebClientAdapter.create(webClient);
// or RestTemplate...
RestTemplate restTemplate = new RestTemplate();
RestTemplateAdapter adapter = RestTemplateAdapter.create(restTemplate);
HttpServiceProxyFactory factory = HttpServiceProxyFactory.builderFor(adapter).build();
----
Now, you're ready to create client proxies:
[source,java,indent=0,subs="verbatim,quotes"]
----
RepositoryService service = factory.createClient(RepositoryService.class);
// Use service methods for remote calls...
----
[[rest-http-interface-method-parameters]] [[rest-http-interface-method-parameters]]
=== Method Parameters === Method Parameters
Annotated, HTTP exchange methods support flexible method signatures with the following `@HttpExchange` methods support flexible method signatures with the following inputs:
method parameters:
[cols="1,2", options="header"] [cols="1,2", options="header"]
|=== |===
| Method argument | Description | Method parameter | Description
| `URI` | `URI`
| Dynamically set the URL for the request, overriding the annotation's `url` attribute. | Dynamically set the URL for the request, overriding the annotation's `url` attribute.
@ -1005,26 +1002,26 @@ parameter annotation) is set to `false`, or the parameter is marked optional as
[[rest-http-interface.custom-resolver]] [[rest-http-interface.custom-resolver]]
=== Custom argument resolver === Custom Arguments
For more complex cases, HTTP interfaces do not support `RequestEntity` types as method parameters. You can configure a custom `HttpServiceArgumentResolver`. The example interface below
This would take over the entire HTTP request and not improve the semantics of the interface. uses a custom `Search` method parameter type:
Instead of adding many method parameters, developers can combine them into a custom type
and configure a dedicated `HttpServiceArgumentResolver` implementation.
In the following HTTP interface, we are using a custom `Search` type as a parameter:
include-code::./CustomHttpServiceArgumentResolver[tag=httpinterface,indent=0] include-code::./CustomHttpServiceArgumentResolver[tag=httpinterface,indent=0]
We can implement our own `HttpServiceArgumentResolver` that supports our custom `Search` type A custom argument resolver could be implemented like this:
and writes its data in the outgoing HTTP request.
include-code::./CustomHttpServiceArgumentResolver[tag=argumentresolver,indent=0] include-code::./CustomHttpServiceArgumentResolver[tag=argumentresolver,indent=0]
Finally, we can use this argument resolver during the setup and use our HTTP interface. To configure the custom argument resolver:
include-code::./CustomHttpServiceArgumentResolver[tag=usage,indent=0] include-code::./CustomHttpServiceArgumentResolver[tag=usage,indent=0]
TIP: By default, `RequestEntity` is not supported as a method parameter, instead encouraging
the use of more fine-grained method parameters for individual parts of the request.
[[rest-http-interface-return-values]] [[rest-http-interface-return-values]]
=== Return Values === Return Values
@ -1101,61 +1098,35 @@ underlying HTTP client, which operates at a lower level and provides more contro
[[rest-http-interface-exceptions]] [[rest-http-interface-exceptions]]
=== Error Handling === Error Handling
To customize error response handling, you need to configure the underlying HTTP client. To customize error handling for HTTP Service client proxies, you can configure the
underlying client as needed. By default, clients raise an exception for 4xx and 5xx HTTP
For `RestClient`: status codes. To customize this, register a response status handler that applies to all
responses performed through the client as follows:
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"] [source,java,indent=0,subs="verbatim,quotes"]
---- ----
// For RestClient
RestClient restClient = RestClient.builder() RestClient restClient = RestClient.builder()
.defaultStatusHandler(HttpStatusCode::isError, (request, response) -> ...) .defaultStatusHandler(HttpStatusCode::isError, (request, response) -> ...)
.build(); .build();
RestClientAdapter adapter = RestClientAdapter.create(restClient); 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 // or for WebClient...
`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() WebClient webClient = WebClient.builder()
.defaultStatusHandler(HttpStatusCode::isError, resp -> ...) .defaultStatusHandler(HttpStatusCode::isError, resp -> ...)
.build(); .build();
WebClientAdapter adapter = WebClientAdapter.create(webClient); 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 // or for RestTemplate...
`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 restTemplate = new RestTemplate();
restTemplate.setErrorHandler(myErrorHandler); restTemplate.setErrorHandler(myErrorHandler);
RestTemplateAdapter adapter = RestTemplateAdapter.create(restTemplate); RestTemplateAdapter adapter = RestTemplateAdapter.create(restTemplate);
HttpServiceProxyFactory factory = HttpServiceProxyFactory.builderFor(adapter).build(); HttpServiceProxyFactory factory = HttpServiceProxyFactory.builderFor(adapter).build();
---- ----
For more details and options, see the Javadoc of `setErrorHandler` in `RestTemplate` and For more details and options such as suppressing error status codes, see the reference
the `ResponseErrorHandler` hierarchy. documentation for each client, as well as the Javadoc of `defaultStatusHandler` in
`RestClient.Builder` or `WebClient.Builder`, and the `setErrorHandler` of `RestTemplate`.