Most Spring Boot tutorials focus on building REST APIs. But in practice, most applications also need to consume them — calling third‑party services, microservices in your own stack, or internal APIs. Spring's RestTemplate is the traditional way to do this, and while Spring 5 introduced the reactive WebClient, RestTemplate is still widely used and the right choice for blocking, synchronous HTTP calls in servlet‑based applications.
Setup
No extra dependency needed for RestTemplate — it's part of spring-boot-starter-web. The typical setup uses RestTemplateBuilder, which Spring Boot auto‑configures and injects:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
The examples here also use Jackson's JsonNode to work with JSON responses generically, which is already included through the web starter.
Example 1: Fetching GitHub API Endpoints
GitHub's root API endpoint returns a map of all available API URLs. This makes a good first example because it returns structured JSON with no authentication required:
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.core.annotation.Order;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import com.fasterxml.jackson.databind.JsonNode;
import java.util.Iterator;
import java.util.Map;
@Component
@Order(1)
public class GithubEndpointLoader implements CommandLineRunner {
private final RestTemplate restTemplate;
public GithubEndpointLoader(RestTemplateBuilder builder) {
this.restTemplate = builder.build();
}
@Override
public void run(String... args) {
String url = "https://api.github.com";
ResponseEntity<JsonNode> response =
restTemplate.getForEntity(url, JsonNode.class);
System.out.println("GitHub API Endpoints:");
Iterator<Map.Entry<String, JsonNode>> fields = response.getBody().fields();
while (fields.hasNext()) {
Map.Entry<String, JsonNode> entry = fields.next();
System.out.println(" " + entry.getKey() + " -> " + entry.getValue().asText());
}
}
}
getForEntity(url, responseType) makes a GET request and wraps the response in a ResponseEntity, giving you access to the status code, headers, and body. Using JsonNode.class as the response type tells Jackson to parse the JSON into a generic tree structure — useful when you don't want to create a specific class just to parse a response.
Example 2: Fetching User Repositories
A more realistic example — fetching a list of repositories for a specific GitHub user. The response is a JSON array, so you need to check before iterating:
@Component
@Order(2)
public class GithubRepoLoader implements CommandLineRunner {
private final RestTemplate restTemplate;
public GithubRepoLoader(RestTemplateBuilder builder) {
this.restTemplate = builder.build();
}
@Override
public void run(String... args) {
String url = "https://api.github.com/users/bootng/repos";
ResponseEntity<JsonNode> response =
restTemplate.getForEntity(url, JsonNode.class);
JsonNode body = response.getBody();
System.out.println("Repositories:");
if (body != null && body.isArray()) {
for (JsonNode repo : body) {
System.out.println(" " + repo.get("name").asText()
+ " — " + repo.get("description").asText());
}
}
}
}
The @Order annotations on both classes control which CommandLineRunner executes first. Order 1 runs before Order 2.
Deserializing into a POJO Instead
For responses with a known structure, mapping to a POJO is cleaner than working with JsonNode:
public class GithubRepo {
private String name;
private String description;
private int stargazersCount;
// getters and setters
}
// Use an array type as the response type
ResponseEntity<GithubRepo[]> response =
restTemplate.getForEntity(url, GithubRepo[].class);
GithubRepo[] repos = response.getBody();
for (GithubRepo repo : repos) {
System.out.println(repo.getName());
}
Jackson maps JSON field names to Java field names. If the JSON uses snake_case (stargazers_count) and your Java uses camelCase (stargazersCount), add @JsonProperty("stargazers_count") to the field or configure an object mapper with MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES.
POST, PUT, DELETE
RestTemplate supports all HTTP methods:
// POST — send a request body, receive a response body
ResponseEntity<Blog> created =
restTemplate.postForEntity("/api/blogs", newBlog, Blog.class);
// PUT — update a resource
restTemplate.put("/api/blogs/{id}", updatedBlog, blogId);
// DELETE — remove a resource
restTemplate.delete("/api/blogs/{id}", blogId);
Error Handling
By default, RestTemplate throws HttpClientErrorException (4xx responses) and HttpServerErrorException (5xx responses). You can handle them explicitly:
try {
ResponseEntity<Blog> response =
restTemplate.getForEntity("/api/blogs/nonexistent", Blog.class);
} catch (HttpClientErrorException.NotFound e) {
System.out.println("Blog not found: " + e.getStatusCode());
} catch (HttpServerErrorException e) {
System.out.println("Server error: " + e.getStatusCode());
}
For more control, implement a custom ResponseErrorHandler and set it on the RestTemplate instance.
Running the Application
mvn clean install
mvn spring-boot:run
The console output shows the GitHub API endpoints (from the first runner) followed by the repository list (from the second runner), ordered by the @Order annotations.
RestTemplate vs WebClient
Spring 5 introduced WebClient as the modern, non‑blocking alternative. RestTemplate is still maintained but no longer actively developed with new features. For most existing Spring MVC applications, RestTemplate is perfectly fine. Switch to WebClient if you're building reactive applications with Spring WebFlux or need non‑blocking I/O for performance reasons.
Summary
Spring Boot's RestTemplate provides a straightforward API for making HTTP requests from Java. Inject RestTemplateBuilder, call build() to get an instance, then use methods like getForEntity() to make requests. The response comes back as a ResponseEntity containing status, headers, and body. Use JsonNode.class as the response type for flexible generic JSON parsing, or map to a POJO for known response structures. For sequenced startup operations, implement CommandLineRunner with @Order to control execution sequence.
Happy coding!
No comments :
Post a Comment
Please leave your message queries or suggetions.
Note: Only a member of this blog may post a comment.