Skip to content

Commit b439c07

Browse files
committed
Document HTTP Service client support
Closes gh-47179
1 parent 74a5d13 commit b439c07

File tree

16 files changed

+563
-0
lines changed

16 files changed

+563
-0
lines changed

documentation/spring-boot-docs/src/docs/antora/modules/reference/pages/io/rest-client.adoc

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,3 +306,163 @@ You can also define javadoc:org.springframework.web.client.ApiVersionInserter[]
306306

307307
TIP: API versioning is also supported on the server-side.
308308
See the xref:web/servlet.adoc#web.servlet.spring-mvc.api-versioning[Spring MVC] and xref:web/reactive.adoc#web.reactive.webflux.api-versioning[Spring WebFlux] sections for details.
309+
310+
311+
312+
[[io.rest-client.httpservice]]
313+
== HTTP Service Interface Clients
314+
315+
Instead of directly using a javadoc:org.springframework.web.client.RestClient[] or javadoc:org.springframework.web.reactive.function.client.WebClient[] to call an HTTP service, it's also possible to call them using annotated Java interfaces.
316+
317+
HTTP Service interfaces defines a service contract by using methods that are annotated with javadoc:org.springframework.web.service.annotation.HttpExchange[format=annotation], or more typically the method specific variants (javadoc:org.springframework.web.service.annotation.GetExchange[format=annotation], javadoc:org.springframework.web.service.annotation.PostExchange[format=annotation], javadoc:org.springframework.web.service.annotation.DeleteExchange[format=annotation], etc).
318+
319+
For example, the following code defines an HTTP Service for an an "`echo`" API that will return a JSON object containing an echo of the request.
320+
321+
include-code::EchoService[]
322+
323+
More details about how to develop HTTP Service interface clients can be found in the {url-spring-framework-docs}/integration/rest-clients.html#rest-http-interface[Spring Framework reference documentation].
324+
325+
326+
327+
[[io.rest-client.httpservice.importing]]
328+
=== Importing HTTP Services
329+
330+
In order to use an HTTP Service interface as client you need to import it.
331+
One way to achieve this is to use the javadoc:org.springframework.web.service.registry.ImportHttpServices[format=annotation] annotation, typically on your main application class.
332+
You can use the annotation to import specific classes, or scan for classes to import from specific packages.
333+
334+
For example, the following configuration will scan for HTTP Service interfaces in the `com.example.myclients` package:
335+
336+
include-code::MyApplication[]
337+
338+
339+
340+
[[io.rest-client.httpservice.groups]]
341+
=== Service Client Groups
342+
343+
Hard-coding absolute URLs in javadoc:org.springframework.web.service.annotation.HttpExchange[format=annotation] annotations is often not ideal in production applications.
344+
Instead, you will typically want to give the HTTP Service client a logical name in your code, and then lookup a URL from a property based on that name.
345+
346+
HTTP Service clients allow you to do this by registering them into named groups.
347+
An HTTP Service group is a collection of HTTP Service interfaces that all share common features.
348+
349+
For example, we may want to define an "`echo`" group to use for HTTP Service clients that call `\https://echo.zuplo.io`.
350+
351+
NOTE: HTTP Service groups can be used to define more than just URLs.
352+
For example, your group could define connection timeouts and SSL settings.
353+
You can also associate client customization logic to a group, such as adding code to insert required authorization headers.
354+
355+
To associate an HTTP Service interface with a group when using javadoc:org.springframework.web.service.registry.ImportHttpServices[format=annotation] you can use the `group` attribute.
356+
357+
For example, if we assume our example above is organized in such a way that all HTTP Service interfaces in the `com.example.myclients` package belong to the `echo` group.
358+
We first remove the hardcoded URL from the service interface:
359+
360+
include-code::EchoService[]
361+
362+
We can then write:
363+
364+
include-code::MyApplication[]
365+
366+
And finally we can then use a `base-url` property to link the `echo` group to an actual URL.
367+
368+
For a `RestClient` backed HTTP Service client this would be:
369+
370+
[configprops,yaml]
371+
----
372+
spring:
373+
http:
374+
client:
375+
service:
376+
group:
377+
echo:
378+
base-url: "https://echo.zuplo.io"
379+
----
380+
381+
For a `WebClient` backed HTTP Service client, we'd use the reactive variant:
382+
383+
[configprops,yaml]
384+
----
385+
spring:
386+
http:
387+
reactiveclient:
388+
service:
389+
group:
390+
echo:
391+
base-url: "https://echo.zuplo.io"
392+
----
393+
394+
TIP: HTTP Service clients will be associated with a group named "`default`" if you don't specify a group.
395+
396+
[NOTE]
397+
====
398+
If you have multiple HTTP Service interfaces in the same package that need to be associated with different groups you can list them individually.
399+
The javadoc:org.springframework.web.service.registry.ImportHttpServices[format=annotation] is repeatable and the `types` attributes allows you to import individual classes.
400+
401+
For example:
402+
403+
include-code::repeat/MyApplication[]
404+
====
405+
406+
407+
408+
[[io.rest-client.httpservice.properties]]
409+
=== Configuration Properties
410+
411+
Configuration properties for HTTP Services can be specified under `spring.http.client.service` for `RestClient` backed clients and `spring.http.reactiveclient.service` for `WebClient` backed clients.
412+
413+
You can use properties to configure aspects such as:
414+
415+
* The base URL.
416+
* Any default default headers that should be sent.
417+
* API versioning configuration.
418+
* Redirect settings.
419+
* Connection and read timeouts.
420+
* SSL bundles to use.
421+
422+
Properties are hierarchical and can be specified per-group, or for all HTTP Service clients.
423+
Some properties can also be specified as xref:/io/rest-client.adoc#io.rest-client.clienthttprequestfactory.configuration[global client configuration], so that they are considered for both HTTP Service clients and direct use of `RestClient` or `WebClient`.
424+
425+
For example, the properties below will:
426+
427+
* Configure all HTTP Service client and `RestClient` beans to use a one second connect timeout (unless otherwise overridden).
428+
* Configure all HTTP Service clients to use a two second read timeout (unless otherwise overridden).
429+
* Configure HTTP Service clients in the "`echo`" group to use a specific base URL.
430+
431+
[configprops,yaml]
432+
----
433+
spring:
434+
http:
435+
client:
436+
connect-timeout: 1s
437+
service:
438+
read-timeout: 2s;
439+
group:
440+
echo:
441+
base-url: "https://echo.zuplo.io"
442+
----
443+
444+
445+
446+
[[io.rest-client.httpservice.customization]]
447+
=== Customization
448+
449+
If you need to customize HTTP Service clients beyond basic properties, you can use an HTTP Service group configurer.
450+
For `RestClient` backed HTTP Service clients, you can declare a bean that implements javadoc:org.springframework.web.client.support.RestClientHttpServiceGroupConfigurer[].
451+
For `WebClient` backed HTTP Service clients you can declare a bean that implements javadoc:org.springframework.web.reactive.function.client.support.WebClientHttpServiceGroupConfigurer[].
452+
453+
Both work in the same way and will be automatically applied by Spring Boot's auto-configuraiton.
454+
455+
For example, the following configuration would add a group customizer that adds an HTTP header to each outgoing request containing the group name:
456+
457+
include-code::MyHttpServiceGroupConfiguration[]
458+
459+
460+
461+
[[io.rest-client.httpservice.advanced-configuration]]
462+
=== Advanced Configuration
463+
464+
As well as the javadoc:org.springframework.web.service.registry.ImportHttpServices[format=annotation] annotation, Spring Framework also offers an javadoc:org.springframework.web.service.registry.AbstractHttpServiceRegistrar[] class.
465+
You can javadoc:org.springframework.context.annotation.Import[format=annotation] your own extension of this class to perform programmatic configuration.
466+
For more details, see {url-spring-framework-docs}/integration/rest-clients.html#rest-http-interface-group-config[Spring Framework's reference documenation].
467+
468+
Regardless of which method you use to register HTTP Service clients, Spring Boot's support remains the same.
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
* Copyright 2012-present the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.docs.io.restclient.httpservice;
18+
19+
import java.util.Map;
20+
21+
import org.springframework.web.bind.annotation.RequestBody;
22+
import org.springframework.web.service.annotation.HttpExchange;
23+
import org.springframework.web.service.annotation.PostExchange;
24+
25+
@HttpExchange(url = "https://echo.zuplo.io")
26+
public interface EchoService {
27+
28+
@PostExchange
29+
Map<?, ?> echo(@RequestBody Map<String, String> message);
30+
31+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* Copyright 2012-present the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.docs.io.restclient.httpservice.customization;
18+
19+
import org.springframework.context.annotation.Bean;
20+
import org.springframework.context.annotation.Configuration;
21+
import org.springframework.web.client.support.RestClientHttpServiceGroupConfigurer;
22+
23+
@Configuration(proxyBeanMethods = false)
24+
public class MyHttpServiceGroupConfiguration {
25+
26+
@Bean
27+
RestClientHttpServiceGroupConfigurer myHttpServiceGroupConfigurer() {
28+
return (groups) -> groups.forEachClient((group, clientBuilder) -> {
29+
String groupName = group.name();
30+
clientBuilder.defaultHeader("service-group", groupName);
31+
});
32+
}
33+
34+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
* Copyright 2012-present the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.docs.io.restclient.httpservice.groups;
18+
19+
import java.util.Map;
20+
21+
import org.springframework.web.bind.annotation.RequestBody;
22+
import org.springframework.web.service.annotation.PostExchange;
23+
24+
public interface EchoService {
25+
26+
@PostExchange
27+
Map<?, ?> echo(@RequestBody Map<String, String> message);
28+
29+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
* Copyright 2012-present the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.docs.io.restclient.httpservice.groups;
18+
19+
import org.springframework.boot.SpringApplication;
20+
import org.springframework.boot.autoconfigure.SpringBootApplication;
21+
import org.springframework.web.service.registry.ImportHttpServices;
22+
23+
@SpringBootApplication
24+
@ImportHttpServices(group = "echo", basePackages = "com.example.myclients")
25+
public class MyApplication {
26+
27+
public static void main(String[] args) {
28+
SpringApplication.run(MyApplication.class, args);
29+
}
30+
31+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
* Copyright 2012-present the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.docs.io.restclient.httpservice.groups.repeat;
18+
19+
import java.util.Map;
20+
21+
import org.springframework.web.bind.annotation.RequestBody;
22+
import org.springframework.web.service.annotation.PostExchange;
23+
24+
public interface EchoService {
25+
26+
@PostExchange("/echo")
27+
Map<?, ?> echo(@RequestBody Map<String, String> message);
28+
29+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
* Copyright 2012-present the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.docs.io.restclient.httpservice.groups.repeat;
18+
19+
import org.springframework.boot.SpringApplication;
20+
import org.springframework.boot.autoconfigure.SpringBootApplication;
21+
import org.springframework.web.service.registry.ImportHttpServices;
22+
23+
@SpringBootApplication
24+
@ImportHttpServices(group = "echo", types = EchoService.class)
25+
@ImportHttpServices(group = "other", types = OtherService.class)
26+
public class MyApplication {
27+
28+
public static void main(String[] args) {
29+
SpringApplication.run(MyApplication.class, args);
30+
}
31+
32+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
* Copyright 2012-present the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.docs.io.restclient.httpservice.groups.repeat;
18+
19+
import java.util.Map;
20+
21+
import org.springframework.web.bind.annotation.RequestBody;
22+
import org.springframework.web.service.annotation.PostExchange;
23+
24+
public interface OtherService {
25+
26+
@PostExchange("/other")
27+
Map<?, ?> other(@RequestBody Map<String, String> message);
28+
29+
}

0 commit comments

Comments
 (0)