This library was born as an effort to avoid boilerplate code and making use of Spring Boot's auto-configuration features.
It is based on Spring Cloud Feign but it uses RestTemplate instead of Netflix's Feign and Spring MVC annotations.
As an additional feature, spring-boot-rest-client supports Spring Retry so that HTTP requests can be retried upon either specific HTTP statuses and/or defined Exceptions.
@EnableRestClients
@SpringBootApplication
public class FooApplication {
public static void main(String... args) {
SpringApplication.run(FooApplication.class, args);
}
@RestClient("foo")
interface FooClient {
@RequestMapping
Foo getFoo();
}
}spring:
rest:
client:
services:
foo: http://foo.bar.seYou can later use @Autowired (or constructor injection) and just call fooClient.getFoo() which will
make an HTTP GET call to http://foo.bar.se
@Component
public class RestClientConsumer {
private final FooClient fooClient;
RestClientConsumer(FooClient fooClient) {
this.fooClient = fooClient;
}
public Foo getFoo() {
return fooClient.getFoo();
}
}@RequestMapping values have the following correspondence to the resulting HTTP call:
value()- Path appended to the hostmethod()- The HTTP method (GET is the default)produces()- Value of the Accept headerconsumes()- Value of the Content-Type headerheaders()-String[]of key-value pairs of headers separated by ':'
All HTTP REST methods are supported (GET, POST, PUT, PATCH and DELETE) as well as the following annotations on parameters:
A method parameter with no annotation is expected to be the request body (payload) of the request
if @RequestBody is not specified.
In addition to @RequestMapping, composed variants introduced in Spring MVC 4.3 can also be used.
Check this
for more details.
Spring Boot Rest Template can be also be configured to be used for asynchronous REST calls for which it will instead use
an AsyncRestTemplate bean. It supports both Oracle's CompletableFuture
as well as Spring's ListenableFuture.
@RestClient("foo")
interface FooClient {
@RequestMapping("/{id}")
ListenableFuture<String> foo(@PathVariable("id") String id, @RequestParam("query") String query);
@RequestMapping(value = "/{id}", produces = MediaType.APPLICATION_JSON_VALUE)
CompletableFuture<Foo> getFoo(@PathVariable("id") String id);
}Please note that retry functionality is currently not supported for asynchronous requests.
Generic declarations are supported as long as the "implementing" interface contains a concrete class.
Working example:
interface FooBaseClient<T> {
@GetMapping(value = "/{id}")
T getParameterized(@PathVariable("id") String id);
}@RestClient("foo")
interface FooClient extends FooBaseClient<Foo> {
}If for some reason you do not wish to have the body extracted from your response, you can wrap your response type in either a ResponseEntity as well as an HttpEntity.
@RestClient("foo")
interface FooClient {
@GetMapping
ResponseEntity<String> getEntity();
@GetMapping
HttpEntity<String> getHttpEntity();
}If you wrap your response type in Oracle's JDK 8 Optional,
Spring Boot Rest Client will return an Optional.empty() upon a HTTP 404 NOT FOUND response code.
@RestClient("foo")
interface FooClient {
@GetMapping(produces = MediaType.APPLICATION_JSON_VALUE)
Optional<Foo> getOptional();
}Please note that default methods in interfaces declaring @RestClient are currently not supported.
Specially useful for HATEOAS, you can use the @PostForLocation annotation to
indicate that a POST request should return the Location HTTP header as an URI.
@RestClient("foo")
interface FooClient {
@PostForLocation("/postForLocation")
URI postForLocation(String body);
}Additionally, by including Spring HATEOAS as a dependency, you can use Spring HATEOAS resource support:
public class FooResource extends ResourceSupport {
}@RestClient("foo")
interface FooClient {
@GetMapping(value = "/foo/{id}", produces = MediaTypes.HAL_JSON_VALUE)
FooResource getFoo(@PathVariable("id") String id);
@GetMapping(value = "/foo/{id}", produces = MediaTypes.HAL_JSON_VALUE)
Resource<Foo> getFooWrapped(@PathVariable("id") String id);
@GetMapping(value = "/foos", produces = MediaTypes.HAL_JSON_VALUE)
Resources<FooResource> getFoos();
@GetMapping(value = "/foos", produces = MediaTypes.HAL_JSON_VALUE)
PagedResources<FooResource> getPagedFoos();
}The rest client library can be used with Spring Retry. Just by adding the org.springframework.retry:spring-retry
library as a dependency and @EnableRetry in your configuration, the retry functionality will be enabled.
By default, calls are retried on HTTP 503 SERVICE UNAVAILABLE and IOException but you can configure your own:
@RestClient(
value = "foo",
retryOn = {HttpStatus.SERVICE_UNAVAILABLE, HttpStatus.BAD_GATEWAY},
retryOnException = SocketTimeoutException.class)
interface FooClient {
@RestClient("/foos")
List<Foo> getFooList();
}Furthermore, global retry settings can be configured by adding values to application.yml. Below, the default values
are shown:
spring:
rest:
client:
services:
foo: http://foo.bar.se
retry:
max-attempts: 3
back-off:
delay: 1000
max-delay: 0
multiplier: 0.0
random: falseRefer to Spring Retry for more information about what the values refer to.
- The library will create a
RestTemplateand aAsyncRestTemplateSpring beans if not already present using a RestTemplateBuilder - The library is non-intrusive. That means that if you want the spring-retry functionality you'll need to include it and all of its dependencies
@RestClientalso accepts an optionalurl()parameter which can be either a hardcoded value or a SpEL expression
- Add option to disable retry on either clients or specific methods
- Support
@Recovermethod as specified in Spring Retry when retries are exhausted