3
3
4
4
The Spring Framework provides the following choices for making calls to REST endpoints:
5
5
6
- * xref:integration/rest-clients.adoc#rest-restclient[`RestClient`] - synchronous client with a fluent API.
7
- * xref:integration/rest-clients.adoc#rest-webclient[`WebClient`] - non-blocking, reactive client with fluent API.
8
- * xref:integration/rest-clients.adoc#rest-resttemplate[`RestTemplate`] - synchronous client with template method API.
9
- * xref:integration/rest-clients.adoc#rest-http-interface[HTTP Interface] - annotated interface with generated, dynamic proxy implementation.
6
+ * xref:integration/rest-clients.adoc#rest-restclient[`RestClient`] -- synchronous client with a fluent API
7
+ * xref:integration/rest-clients.adoc#rest-webclient[`WebClient`] -- non-blocking, reactive client with fluent API
8
+ * xref:integration/rest-clients.adoc#rest-resttemplate[`RestTemplate`] -- synchronous client with template method API
9
+ * xref:integration/rest-clients.adoc#rest-http-interface[HTTP Interface Clients ] -- annotated interface backed by generated proxy
10
10
11
11
12
12
[[rest-restclient]]
@@ -857,15 +857,17 @@ It can be used to migrate from the latter to the former.
857
857
858
858
859
859
[[rest-http-interface]]
860
- == HTTP Interface
860
+ == HTTP Interface Clients
861
861
862
- The Spring Framework lets you define an HTTP service as a Java interface with
863
- `@HttpExchange` methods. You can pass such an interface to `HttpServiceProxyFactory`
864
- to create a proxy which performs requests through an HTTP client such as `RestClient`
865
- or `WebClient`. You can also implement the interface from an `@Controller` for server
866
- request handling.
862
+ You can define an HTTP Service as a Java interface with `@HttpExchange` methods, and use
863
+ `HttpServiceProxyFactory` to create a client proxy from it for remote access over HTTP via
864
+ `RestClient`, `WebClient`, or `RestTemplate`. On the server side, an `@Controller` class
865
+ can implement the same interface to handle requests with
866
+ xref:web/webmvc/mvc-controller/ann-requestmapping.adoc#mvc-ann-httpexchange-annotation[@HttpExchange]
867
+ controller methods.
867
868
868
- Start by creating the interface with `@HttpExchange` methods:
869
+
870
+ First, create the Java interface:
869
871
870
872
[source,java,indent=0,subs="verbatim,quotes"]
871
873
----
@@ -879,69 +881,64 @@ Start by creating the interface with `@HttpExchange` methods:
879
881
}
880
882
----
881
883
882
- Now you can create a proxy that performs requests when methods are called.
883
-
884
- For `RestClient`:
884
+ Optionally, use `@HttpExchange` at the type level to declare common attributes for all methods:
885
885
886
886
[source,java,indent=0,subs="verbatim,quotes"]
887
887
----
888
- RestClient restClient = RestClient.builder().baseUrl("https://api.github.com/").build();
889
- RestClientAdapter adapter = RestClientAdapter.create(restClient);
890
- HttpServiceProxyFactory factory = HttpServiceProxyFactory.builderFor(adapter).build();
888
+ @HttpExchange(url = "/repos/{owner}/{repo}", accept = "application/vnd.github.v3+json")
889
+ public interface RepositoryService {
891
890
892
- RepositoryService service = factory.createClient(RepositoryService.class);
893
- ----
891
+ @GetExchange
892
+ Repository getRepository(@PathVariable String owner, @PathVariable String repo);
894
893
895
- For `WebClient`:
894
+ @PatchExchange(contentType = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
895
+ void updateRepository(@PathVariable String owner, @PathVariable String repo,
896
+ @RequestParam String name, @RequestParam String description, @RequestParam String homepage);
896
897
897
- [source,java,indent=0,subs="verbatim,quotes"]
898
+ }
898
899
----
899
- WebClient webClient = WebClient.builder().baseUrl("https://api.github.com/").build();
900
- WebClientAdapter adapter = WebClientAdapter.create(webClient);
901
- HttpServiceProxyFactory factory = HttpServiceProxyFactory.builderFor(adapter).build();
902
900
903
- RepositoryService service = factory.createClient(RepositoryService.class);
904
- ----
905
901
906
- For `RestTemplate `:
902
+ Next, configure the client and create the `HttpServiceProxyFactory `:
907
903
908
904
[source,java,indent=0,subs="verbatim,quotes"]
909
905
----
906
+ // Using RestClient...
907
+
908
+ RestClient restClient = RestClient.create("...");
909
+ RestClientAdapter adapter = RestClientAdapter.create(restClient);
910
+
911
+ // or WebClient...
912
+
913
+ WebClient webClient = WebClient.create("...");
914
+ WebClientAdapter adapter = WebClientAdapter.create(webClient);
915
+
916
+ // or RestTemplate...
917
+
910
918
RestTemplate restTemplate = new RestTemplate();
911
- restTemplate.setUriTemplateHandler(new DefaultUriBuilderFactory("https://api.github.com/"));
912
919
RestTemplateAdapter adapter = RestTemplateAdapter.create(restTemplate);
913
- HttpServiceProxyFactory factory = HttpServiceProxyFactory.builderFor(adapter).build();
914
920
915
- RepositoryService service = factory.createClient(RepositoryService.class );
921
+ HttpServiceProxyFactory factory = HttpServiceProxyFactory.builderFor(adapter).build( );
916
922
----
917
923
918
- `@HttpExchange` is supported at the type level where it applies to all methods :
924
+ Now, you're ready to create client proxies :
919
925
920
926
[source,java,indent=0,subs="verbatim,quotes"]
921
927
----
922
- @HttpExchange(url = "/repos/{owner}/{repo}", accept = "application/vnd.github.v3+json")
923
- public interface RepositoryService {
924
-
925
- @GetExchange
926
- Repository getRepository(@PathVariable String owner, @PathVariable String repo);
927
-
928
- @PatchExchange(contentType = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
929
- void updateRepository(@PathVariable String owner, @PathVariable String repo,
930
- @RequestParam String name, @RequestParam String description, @RequestParam String homepage);
931
-
932
- }
928
+ RepositoryService service = factory.createClient(RepositoryService.class);
929
+ // Use service methods for remote calls...
933
930
----
934
931
935
932
933
+
936
934
[[rest-http-interface-method-parameters]]
937
935
=== Method Parameters
938
936
939
- Annotated, HTTP exchange methods support flexible method signatures with the following
940
- method parameters:
937
+ `@HttpExchange` methods support flexible method signatures with the following inputs:
941
938
942
939
[cols="1,2", options="header"]
943
940
|===
944
- | Method argument | Description
941
+ | Method parameter | Description
945
942
946
943
| `URI`
947
944
| 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
1005
1002
1006
1003
1007
1004
[[rest-http-interface.custom-resolver]]
1008
- === Custom argument resolver
1005
+ === Custom Arguments
1009
1006
1010
- For more complex cases, HTTP interfaces do not support `RequestEntity` types as method parameters.
1011
- This would take over the entire HTTP request and not improve the semantics of the interface.
1012
- Instead of adding many method parameters, developers can combine them into a custom type
1013
- and configure a dedicated `HttpServiceArgumentResolver` implementation.
1014
-
1015
- In the following HTTP interface, we are using a custom `Search` type as a parameter:
1007
+ You can configure a custom `HttpServiceArgumentResolver`. The example interface below
1008
+ uses a custom `Search` method parameter type:
1016
1009
1017
1010
include-code::./CustomHttpServiceArgumentResolver[tag=httpinterface,indent=0]
1018
1011
1019
- We can implement our own `HttpServiceArgumentResolver` that supports our custom `Search` type
1020
- and writes its data in the outgoing HTTP request.
1012
+ A custom argument resolver could be implemented like this:
1021
1013
1022
1014
include-code::./CustomHttpServiceArgumentResolver[tag=argumentresolver,indent=0]
1023
1015
1024
- Finally, we can use this argument resolver during the setup and use our HTTP interface.
1016
+ To configure the custom argument resolver:
1025
1017
1026
1018
include-code::./CustomHttpServiceArgumentResolver[tag=usage,indent=0]
1027
1019
1020
+ TIP: By default, `RequestEntity` is not supported as a method parameter, instead encouraging
1021
+ the use of more fine-grained method parameters for individual parts of the request.
1022
+
1023
+
1024
+
1028
1025
[[rest-http-interface-return-values]]
1029
1026
=== Return Values
1030
1027
@@ -1101,61 +1098,35 @@ underlying HTTP client, which operates at a lower level and provides more contro
1101
1098
[[rest-http-interface-exceptions]]
1102
1099
=== Error Handling
1103
1100
1104
- To customize error response handling, you need to configure the underlying HTTP client.
1105
-
1106
- For `RestClient`:
1107
-
1108
- By default, `RestClient` raises `RestClientException` for 4xx and 5xx HTTP status codes.
1109
- To customize this, register a response status handler that applies to all responses
1110
- performed through the client:
1101
+ To customize error handling for HTTP Service client proxies, you can configure the
1102
+ underlying client as needed. By default, clients raise an exception for 4xx and 5xx HTTP
1103
+ status codes. To customize this, register a response status handler that applies to all
1104
+ responses performed through the client as follows:
1111
1105
1112
1106
[source,java,indent=0,subs="verbatim,quotes"]
1113
1107
----
1108
+ // For RestClient
1114
1109
RestClient restClient = RestClient.builder()
1115
1110
.defaultStatusHandler(HttpStatusCode::isError, (request, response) -> ...)
1116
1111
.build();
1117
-
1118
1112
RestClientAdapter adapter = RestClientAdapter.create(restClient);
1119
- HttpServiceProxyFactory factory = HttpServiceProxyFactory.builderFor(adapter).build();
1120
- ----
1121
-
1122
- For more details and options, such as suppressing error status codes, see the Javadoc of
1123
- `defaultStatusHandler` in `RestClient.Builder`.
1124
-
1125
- For `WebClient`:
1126
-
1127
- By default, `WebClient` raises `WebClientResponseException` for 4xx and 5xx HTTP status codes.
1128
- To customize this, register a response status handler that applies to all responses
1129
- performed through the client:
1130
1113
1131
- [source,java,indent=0,subs="verbatim,quotes"]
1132
- ----
1114
+ // or for WebClient...
1133
1115
WebClient webClient = WebClient.builder()
1134
1116
.defaultStatusHandler(HttpStatusCode::isError, resp -> ...)
1135
1117
.build();
1136
-
1137
1118
WebClientAdapter adapter = WebClientAdapter.create(webClient);
1138
- HttpServiceProxyFactory factory = HttpServiceProxyFactory.builder(adapter).build();
1139
- ----
1140
1119
1141
- For more details and options, such as suppressing error status codes, see the Javadoc of
1142
- `defaultStatusHandler` in `WebClient.Builder`.
1143
-
1144
- For `RestTemplate`:
1145
-
1146
- By default, `RestTemplate` raises `RestClientException` for 4xx and 5xx HTTP status codes.
1147
- To customize this, register an error handler that applies to all responses
1148
- performed through the client:
1149
-
1150
- [source,java,indent=0,subs="verbatim,quotes"]
1151
- ----
1120
+ // or for RestTemplate...
1152
1121
RestTemplate restTemplate = new RestTemplate();
1153
1122
restTemplate.setErrorHandler(myErrorHandler);
1154
-
1123
+
1155
1124
RestTemplateAdapter adapter = RestTemplateAdapter.create(restTemplate);
1125
+
1156
1126
HttpServiceProxyFactory factory = HttpServiceProxyFactory.builderFor(adapter).build();
1157
1127
----
1158
1128
1159
- For more details and options, see the Javadoc of `setErrorHandler` in `RestTemplate` and
1160
- the `ResponseErrorHandler` hierarchy.
1129
+ For more details and options such as suppressing error status codes, see the reference
1130
+ documentation for each client, as well as the Javadoc of `defaultStatusHandler` in
1131
+ `RestClient.Builder` or `WebClient.Builder`, and the `setErrorHandler` of `RestTemplate`.
1161
1132
0 commit comments