Make HTTP requests in a Java application using Spring RestTemplate

Make HTTP requests in a Java application using Spring RestTemplate
Photo by Saad Chaudhry / Unsplash

Photo by Saad Chaudhry / Unsplash

In a real-world backend web application, you may need to make a synchronous call to another backend. This can be an internal service (in a microservice application ) or an external service (calling the Currency Exchange Rates  API).

In this case, you need an HTTP client library to build your request, send it, wait for a response, parse it and use the result in our business logic.

In the Java ecosystem, there are various HTTP client libraries like Retrofit, Feign, etc... The Spring Framework comes with a library called RestTemplate.

We will use it because it doesn't require adding a new dependency in the project and being included in the Spring Framework, the update is done along with other core libraries of the Framework.

Prerequisites

You must need these tools installed on your computer to follow this tutorial.

The API to consume

The API we will consume from our Spring Boot application is the one I built on a side project. Check out the project on GitHub.

The API documentation shows the available endpoints of the application. There are endpoints to manage a ToDo list:

  • GET: /tasks - Retrieve all tasks
  • POST: /tasks/create - Create a task
  • PUT: /tasks/{id} - Update a task
  • DELETE: /tasks/{id} - Delete a task
  • GET: /tasks/{id} - Retrieve one task

Let's see how to consume them using the RestTemplate library.

Set up the project

Let's use the online project generator to create a Spring Boot project. If you have IntelliJ, you can create a new project directly with it, I wrote a tutorial on how to do that.

Create a Spring Boot project from IntelliJ
In this post, we will see how to use IntelliJ to create and run a Spring Boot project with Maven dependencies.

The only dependencies we need is the Spring Web:

Create a Spring Boot project from the online starter
Create a Spring Boot project from the online starter

Click on the button "Generate" to download the code and open it in your IDE.

Create a package called controllers and create a file TaskController.java.

Set HTTP Headers

The API required an access token to perform actions in the system. This token must be passed in the header for every request we make.

The header's token key is x-access-token, and the value must be the token generated after successful authentication. I generated a token, so you don't have to; below is the code to set the header:


package com.tericcabrel.httpclient.controllers;

import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.web.client.RestTemplate;

public class TaskController {
  private RestTemplate restTemplate;

  private HttpHeaders headers;

  public TaskController(RestTemplate restTemplate) {
    this.restTemplate = restTemplate;

    headers = new HttpHeaders();
    headers.setContentType(MediaType.APPLICATION_JSON);
    headers.set("x-access-token", "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjYzMmYyZDFjN2UwNWJiMDUwY2RkYTRjYiIsImlhdCI6MTY2NDcxMTI1OSwiZXhwIjoxNjY0NzExMzQ1fQ.4nI9KCat1GJZaMp1RkRHhn99STBg17RA6EwLknMxDO4");
  }

}

The HttpHeaders class provides method to set the value for known header key like Bearer authorization (setBearerAuth()), Accept(setAccept()), etc...

Make a POST

To create a task, let's see what API documentation requires.

API description for the endpoint to create a task
API description for the endpoint to create a task

We must create two models:

  • CreateTaskInput: it encapsulates the data to be sent into the request body.
  • TaskItem: it encapsulates the data returned in the response body.

Create a new package called models, then create the file called CreateTaskInput.java and add the code below:


package com.tericcabrel.httpclient.models;

import com.fasterxml.jackson.annotation.JsonProperty;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

public class CreateTaskInput {
  @JsonProperty(required = true)
  private String title;

  @JsonProperty(required = true)
  private String description;

  @JsonProperty(required = true)
  private String status;

  @JsonProperty(value = "is_important", required = true)
  private boolean isImportant;

  @JsonProperty(value = "date", required = true)
  private String dueDate;

  @JsonProperty(value = "user", required = true)
  private String userId;
  
  // Getters and setters here...  
}

Create another file called TaskItemResponse.java and add the code below:


package com.tericcabrel.httpclient.models;

import com.fasterxml.jackson.annotation.JsonProperty;
import java.time.LocalDateTime;

public class TaskItemResponse {
  @JsonProperty(required = true)
  private String _id;

  @JsonProperty(required = true)
  private String title;

  private String description;

  @JsonProperty(required = true)
  private String status;

  @JsonProperty(value = "is_important", required = true)
  private boolean isImportant;

  @JsonProperty(value = "date", required = true)
  private LocalDateTime dueDate;

  @JsonProperty(value = "user", required = true)
  private String userId;

  private LocalDateTime createdAt;

  private LocalDateTime updatedAt;
  
  // Getters and setters here...
}

Inside the file TaskController.java, add the code below:


@PostMapping("/create-task")
public ResponseEntity createTask() {
    CreateTaskInput createTaskInput = new CreateTaskInput();

    createTaskInput
      .setTitle("My task one")
      .setDescription("Description of my task one")
      .setStatus("Pending")
      .setImportant(true)
      .setDueDate(LocalDateTime.now().plusDays(7))
      .setUserId("632f2d1c7e05bb050cdda4cb");

    HttpEntity<CreateTaskInput> request = new HttpEntity<>(createTaskInput, headers);
    String url = generateUrl("/tasks/create");

    ResponseEntity<TaskItemResponse> result = restTemplate.postForEntity(url, request, TaskItemResponse.class);
    TaskItemResponse task = result.getBody();

    assert task != null;

    return ResponseEntity.ok().body(task);
}
  
  

Start the application with mvn springboot:run; open an HTTP client like Postman and call the endpoint http://localhost:8080/create-task with a POST method.

Execute the request to create a task.
Execute the request to create a task.

We received the API response as expected. The request input is hard-coded in the controller method. You can update the code to receive it from the request body of our Spring Boot application. Check out the article I wrote about

Validate Request Body and Parameter in Spring Boot
In this tutorial, we will see how to use Spring features to validate the request body and request parameters.

Make a GET

Let's retrieve all the tasks of a user. Inside the file TaskController.java, add the code below:


@GetMapping("/all-tasks")
public ResponseEntity allTasks() {
    HttpEntity<CreateTaskInput> request = new HttpEntity<>(headers);
    String url = generateUrl("/tasks");

    ResponseEntity<TaskItemResponse[]> result = restTemplate.exchange(url, HttpMethod.GET, request, TaskItemResponse[].class);
    TaskItemResponse[] tasks = result.getBody();

    assert tasks != null;

    return ResponseEntity.ok().body(tasks);
}
  
  

Re-run the application and call the endpoint http://localhost:8080/all-tasks with a POST method.

Execute the request to retrieve all tasks.
Execute the request to retrieve all tasks.

Note: We used the method exchange() to make the call because its signatures allow passing request headers.

If you are making a call that doesn't have some request header variables, use the method getForEntity(). In our case, it would be:


ResponseEntity<TaskItemResponse[]> result = restTemplate.getForEntity(url, TaskItemResponse[].class);

Make a PUT

As for creating a task, updating a task requires some data to be passed in the request body. Will create a class called UpdateTaskInput.java to encapsulate the input.

Below is the code to update the task and add it to the file TaskController.java:


@PostMapping("/update-task")
public ResponseEntity updateTask() {
    String taskId = "6339a2b6ef26d9d6be2f8df4";

    UpdateTaskInput updateTaskInput = new UpdateTaskInput();

    updateTaskInput
        .setTitle("My Updated task two")
        .setDescription("Updated description of my task two")
        .setStatus("Done")
        .setImportant(false)
        .setDueDate(LocalDateTime.now().plusDays(2));

    HttpEntity<UpdateTaskInput> request = new HttpEntity<>(updateTaskInput, headers);
    String url = generateUrl("/tasks/" + taskId);

    ResponseEntity<TaskItemResponse> result = restTemplate.exchange(url, HttpMethod.PUT, request, TaskItemResponse.class);
    TaskItemResponse task = result.getBody();

    assert task != null;

    return ResponseEntity.ok().body(task);
}

Note: There is a method put() that returns nothing, so if you don't need to do something with the response body, you can use this one.

Make a DELETE

Using delete is relatively easy since it is done we everything we already saw; update the file TaskController.java and add the code below:


@PostMapping("/delete-task")
public ResponseEntity deleteTask() {
    String taskId = "6339a2b6ef26d9d6be2f8df4";

    HttpEntity<UpdateTaskInput> request = new HttpEntity<>(headers);
    String url = generateUrl("/tasks/" + taskId);

    restTemplate.exchange(url, HttpMethod.DELETE, request, Void.class);

    return ResponseEntity.noContent().build();
}
  
  

Handle HTTP Errors

Until now, we show only the happy path; what happens if there is an error?

  • What if the access token is not provided? => Error 401
  • What if bad inputs are provided for creating a task => Error 422
  • When a task doesn't exist => Error 404
HttpClientErrorException thrown by the RestTemplate
HttpClientErrorException thrown by the RestTemplate

The RestTemplate methods throw an exception HttpClientErrorException so we can catch it and behave according to what is returned.

The code to create a task will look like this:


@PostMapping("/create-task")
public ResponseEntity createTask() {
    CreateTaskInput createTaskInput = new CreateTaskInput();

    createTaskInput
      .setTitle("My task two")
      .setDescription("Description of my task two")
      .setStatus("Done")
      .setImportant(false)
      .setDueDate(LocalDateTime.now().plusDays(2))
      .setUserId("632f2d1c7e05bb050cdda4cb");

    HttpEntity<CreateTaskInput> request = new HttpEntity<>(createTaskInput, headers);
    String url = generateUrl("/tasks/create");

    try {
      ResponseEntity<TaskItemResponse> result = restTemplate.postForEntity(url, request, TaskItemResponse.class);
      TaskItemResponse task = result.getBody();

      assert task != null;

      return ResponseEntity.ok().body(task);
    } catch (HttpClientErrorException exception) {
      switch (exception.getRawStatusCode()) {
        case 401:
          // TODO do something
          break;
        case 422:
          // TODO do something
          break;
        default:
          // TODO do something
          break;
      }
    }
}
 
 

Wrap up

RestTemplate allows you to consume external services exposing HTTP endpoints from a Spring Boot application. We covered the basics of performing CRUD operations against a web service.

Check out the documentation to see other methods of the RestTemplate class.

RestTemplate (Spring Framework 5.3.23 API)

You can find the code source on the GitHub repository.

Follow me on Twitter or subscribe to my newsletter to avoid missing the upcoming posts and the tips and tricks I occasionally share.