Spring Boot - RestTemplate

RestTemplate Explained.

RestTemplate is a synchronous client to perform HTTP requests.

RestTemplate Bean

RestTemplate is best decalred as a java bean for reusability.

Create RestTemplate bean and RestTemplate bean with connection timeout

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Configuration
public class RestTemplateConfig {
@Bean
@Primary
public RestTemplate restTemplate() {
return new RestTemplate();
}

@Bean("restTemplateWithTimeout")
public RestTemplate restTemplateWithTimeout() {
int timeout = 5000;
HttpComponentsClientHttpRequestFactory clientHttpRequestFactory
= new HttpComponentsClientHttpRequestFactory();
clientHttpRequestFactory.setConnectTimeout(timeout);
return new RestTemplate(clientHttpRequestFactory);
}
}

RestTemplate Demo

Methods

  • getForObject
  • getForEntity()
  • postForEntity()
  • exchange()

If you need to set the HTTP request headers, you need to use exchange method.

MyService.java - Here are the methods inside a service(MyService) that uses RestTemplate to call jsonplaceholder API endpoint.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
public Post simpleGet() {
log.info("------------RestTemplate.getForEntity()-----------");
try {
ResponseEntity<Post> response = restTemplate.getForEntity(
"https://jsonplaceholder.typicode.com/posts/1", Post.class);
if(response.getStatusCode() == HttpStatus.OK) {
log.info("Response body: " + response.getBody());
return response.getBody();
}
} catch(RestClientException e ) {
log.error("Fail to retrieve Post", e);
throw e;
}
return null;
}

public Post[] exchangeWithUriVariables() {
log.info("------------RestTemplate.getForEntity() getting Array -----------");
try {
Map<String,String> uriVariables = new HashMap<>();
uriVariables.put("key1", "value1");
uriVariables.put("key2", "value2");
HttpHeaders headers = new HttpHeaders();
headers.add(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE);
HttpEntity<Void> requestEntity = new HttpEntity<>(headers);
ResponseEntity<Post[]> response = restTemplate.exchange(
"https://jsonplaceholder.typicode.com/posts", HttpMethod.GET, requestEntity, Post[].class, uriVariables );
if(response.getStatusCode() == HttpStatus.OK) {
log.info("Response body size: " + response.getBody().length);
return response.getBody();
}
} catch(RestClientException e ) {
log.error("Fail to retrieve Post array", e);
throw e;
}
return null;
}

public Post simplePost() {
log.info("------------RestTemplate.postForEntity()-----------");
try {
Post post = new Post();
post.setTitle("Test");
post.setBody("some content");
post.setId(333);
post.setUserId(234);
HttpEntity<Post> requestEntity = new HttpEntity<>(post);
ResponseEntity<Post> response = restTemplate.postForEntity(
"https://jsonplaceholder.typicode.com/posts", requestEntity, Post.class);
if(response.getStatusCode() == HttpStatus.CREATED) {
log.info("Response body: " + response.getBody());
return response.getBody();
}
} catch(RestClientException e ) {
log.error("Fail to post post", e);
throw e;
}
return null;
}

public Post exchangePost() {
log.info("------------RestTemplate.exchange() POST-----------");
try {
Post post = new Post();
post.setTitle("Test");
post.setBody("some content");
post.setId(333);
post.setUserId(234);
HttpHeaders headers = new HttpHeaders();
headers.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
headers.add(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE);
HttpEntity<Post> requestEntity = new HttpEntity<>(post, headers);
ResponseEntity<Post> response = restTemplate.exchange(
"https://jsonplaceholder.typicode.com/posts", HttpMethod.POST, requestEntity, Post.class);
if(response.getStatusCode() == HttpStatus.CREATED) {
log.info("Response body: " + response.getBody());
return response.getBody();
}
} catch(RestClientException e ) {
log.error("Fail to retrieve post", e);
throw e;
}
return null;
}

public Post exchangePut() {
log.info("------------RestTemplate.exchange() PUT-----------");
try {
Post post = new Post();
post.setTitle("Test");
post.setBody("some content");
post.setId(333);
post.setUserId(234);
HttpEntity<Post> requestEntity = new HttpEntity<>(post);
ResponseEntity<Post> response = restTemplate.exchange(
"https://jsonplaceholder.typicode.com/posts/1", HttpMethod.PUT, requestEntity,
Post.class);
if(response.getStatusCode() == HttpStatus.OK) {
log.info("Response body: " + response.getBody());
return response.getBody();
}
} catch(RestClientException e ) {
log.error("Fail to put Post", e);
throw e;
}
return null;
}

public void simpleDelete() {
log.info("------------RestTemplate.delete()-----------");
try {
restTemplate.delete("https://jsonplaceholder.typicode.com/posts/1");
log.info("delete successfully");
} catch (RestClientException e) {
log.error("Fail to delete post", e);
throw e;
}
}

Interceptor

Spring RestTemplate allows us to add interceptors that implement ClientHttpRequestInterceptor interface.
The intercept(HttpRequest, byte[], ClientHttpRequestExecution) method of this interface will intercept
the given request and return the response by giving us access to the request, body and execution objects.

Define a RestTemplate Interceptor that logs request headers, request body and response headers

1
2
3
4
5
6
7
8
9
10
11
12
13
@Component
public class RestClientLogInterceptor implements ClientHttpRequestInterceptor {
private static final Logger LOG = LoggerFactory.getLogger(RestClientLogInterceptor.class);

@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
request.getHeaders().forEach((k,v) -> LOG.debug("Request Header[{}]: {}", k, v));
LOG.debug("Request body: {}", new String(reqBody));
ClientHttpResponse response = execution.execute(request, body);
response.getHeaders().forEach((k,v) -> LOG.debug("Response Header[{}]: {}", k, v));
return response;
}
}

create RestTemplate bean with Interceptor

1
2
3
4
5
6
@Bean("restTemplateWithInterceptor")
public RestTemplate restTemplateWithInterceptor() {
RestTemplate restTemplate = new RestTemplate();
restTemplate.setInterceptors(Collections.singletonList(new RestClientLogInterceptor()));
return restTemplate;
}

Testing RestTemplate

Here is some test cases that tests the service class with RestTemplate service calls.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
public class MyServiceTest {
private static RestTemplate restTemplate;
private static MyService myService;

@BeforeEach
public void init () {
restTemplate = mock(RestTemplate.class);
myService = new MyService(restTemplate);
}

@Test
public void testSimpleGet() {
Post responseBody = new Post();
responseBody.setId(1);
responseBody.setTitle("Post Title");
responseBody.setBody("Some Content");
ResponseEntity<Post> response = ResponseEntity.ok(responseBody);
when(restTemplate.getForEntity( anyString(), eq(Post.class))).thenReturn(response);
Post post = myService.simpleGet();
assertEquals(1, post.getId());
assertEquals("Post Title", post.getTitle());
}

@Test
public void testExchangePost() {
Post responseBody = new Post();
responseBody.setId(1);
responseBody.setTitle("Post Title");
responseBody.setBody("Some Content");
ResponseEntity<Post> response = ResponseEntity.status(HttpStatus.CREATED).body(responseBody);
when(restTemplate.exchange( anyString(), eq(HttpMethod.POST), any(HttpEntity.class), eq(Post.class))).thenReturn(response);

// check response
Post post = myService.exchangePost();
assertEquals(1, post.getId());
assertEquals("Post Title", post.getTitle());

// check request eneity argument that is passed to restTemplate method.
ArgumentCaptor<HttpEntity> argCaptor = ArgumentCaptor.forClass(HttpEntity.class);
verify(restTemplate).exchange( anyString(), eq(HttpMethod.POST), argCaptor.capture(), eq(Post.class));
Post reqBody = (Post) argCaptor.getValue().getBody();
assertEquals("Test", reqBody.getTitle());
assertTrue(argCaptor.getValue().getHeaders().getAccept().stream()
.anyMatch( type -> type.toString().equals(MediaType.APPLICATION_JSON_VALUE.toString())));
}
}

WebClient

RestTemplate is synchronous. It will be deprecated in the future version. WebClient is an modern webclient that can perform sync and async requests. see https://www.baeldung.com/spring-5-webclient for more details.

Link to Source Code: https://github.com/xinghua24/SpringBootExamples/tree/master/RestTemplateExample

Reference