Build REST Client using RestTemplate in Spring Boot

In cloud native programming it's all about building microservices that serve other applications or services with a set of RESTful API. Almost every application is dependent on some other application and wants to access the exposed APIs of another service. The caller application has to efficiently create REST calls for dependent applications. Today I will explain with an example how we can create such a REST Client in Spring Boot using the rest template to send HTTP(s) requests. It's better to have an example to explain, so we will use an example code to understand the concepts better. We will build the code for a scenario as we go step by step.

 

Contents

  1. The Scenario

  2. A template for HTTP requests using RestTemplate

  3. Creating HTTP requests using the Rest Template

  4. Using the API Client

 

The Scenario

Let’s assume that we want to write an application that wants to access a book store through its exposed APIs. Let’s name our application book store api client. The application will implement the below interface. The interface contains the getBookDetails() method to fetch details of a specific book by implementing the method. The createBookDetails() method needs to be implemented to create a book with a model object that represents a book. Our application also needs to implement a variant of the createBookDetails() method to be able to create multiple book entries with a payload.

package com.mycomp.myproject.leafpackage.apiClient;

public interface BookStoreApiClient {
  BookModel getBookDetails(String id) throws RuntimeException;
  String createBulkBookDetails(BookModel[] bokModels) throws RuntimeException ;
  String createBookDetails(BookModel bookModel)  throws RuntimeException;
}

A template for HTTP requests using RestTemplate

We will create a spring boot configuration class that will initialize a RestTemplate bean object when the application is started. The initRestTemplate() method that is executed at the application post configuration uses a RestTemplateBuilder. In this example we are building a rest template where a HTTP connection timeout and a read timeout is configured. Every request built using this rest template will have these two configurations. As a result when the timeouts occur a specific exception will trigger in our client application. The client application can act gracefully by handling the exception, for example a retry can be attempted.

package com.mycomp.myproject.leafpackage.config;
 
import org.springframework.web.client.RestTempate;
import javax.annotation.PostConstruct;
import  org.springframework.context.annotation.Configuration;
import  org.springframework.context.annotation.Bean;

//this class will contain also "property source configurer"

@Configuration
public class  MyRestTemplateBuilder {

 private  RestTemplate  restTemplate;

 @PostConstruct
 public void init() {
   initRestTemplate();
 }

 @Bean
 public  RestTemplate getRestTemplate() {return this.restTemplate;}

 private void  initRestTemplate() {
  RestTemplateBuilder builder = new  RestTemplateBuilder();
  this.restTemplate = builder
 .setConnectionTimeout(Duration.ofMillis(5000))
 .setReadTimeOut(Duration.ofMillis(120000))
 .build();
 }
} 

Creating HTTP requests using the Rest Template

This class will make use of the RestTemplate and also implement the interface. Behind each interface method the HTTP request objects will be created and executed. These HTTP request objects will send HTTP requests to the specefic REST endpoints of the book store.


We inject an object of the configuration class MyRestTemplateBuilder and use the getRestTemplate() method on the configuration object.


In the getBookDetails() method we create an HttpEntity of type Get. The HttpEntity object represents an Http request where we can configure certain HTTP headers. We can set authorization headers that will be used by the book store APIs to validate the authentication and authorization. Here in this example we are using an ApiKey header for this purpose. Book store will identify our application using an API key present in this header. The getBookDetails() method is called using a book id. The rest template uses the baseurl and a specific API endpoint named getdetails with the book id. The REST endpoint looks like https://bokstore.url.com/getDetails/{id} . The “{id}” is a placeholder for a parameter that is replaced by the value of the id variable (which is also passed as a parameter). Then in the exchange() method we pass the type of the HTTP method (HttpMethod.GET) and the corresponding HttpEntity object. We also specify that the return value which will be a JSON has to be converted to an object of a class BookModel. This helps spring to convert the JSON to a BookModel object and put the response into a ResponseEntity of type BookModel.

If the response can not be converted to a BookModel then an exception will be thrown.


Similarly in the createBookDetails() and createBulkBookDetails() method we send HTTP post requests to the Post endpoint of the book store. Here, when we create the HttpEntity object we also add the book object(s) that constitutes the request body of the HTTP post request. As a response to a Post request we expect a string just sending and acknowledgement.

package com.mycomp.myproject.leafpackage.apiClient;

import org.springframework.web.client.RestTemplate;
import org.springframework.web.client.HttpClientErrorException ;
 
import org.springframework.web.http.HttpEntity;
import org.springframework.web.http.HttpHeaders;
import org.springframework.web.http.HttpMethod;
import org.springframework.web.http.HttpStatus;
import org.springframework.web.http.ResponseEntity;
 
import javax.inject.Inject;
import org.springframework.stereotype.Service;
 
@Service
public class BookStoreApiClientImpl implements  BookStoreApiClient {
 
  //do not use private, otherwise difficult to Mock this dependency in unit test
  @Inject 
  MyRestTemplateBuilder restTemplateBuilder;
 
  private final static String baseUrl = "https://bokstore.url.com/" 
 
  public BookStoreApiClientImpl() {super();}
 
 BookModel getBookDetails(String id) throws RuntimeException {
 
   RestTemplate template =  restTemplateBuilder.getRestTemplate();
   HttpEntity<?> httpEntity = createHttpEntityForGet();
   try {
    ResponseEntity<BookModel> response = template.exchange(baseUrl +"getdetails/{id}", 
 						HttpMethod.GET,httpEntity,BookModel.class,id);
     return response.getBody();
   } catch(HttpClientErrorException e) {
     logger.error(e.getMessage(), e);
     throw RuntimeException(e.getMessage());
   }
 }
 
 String createBulkBookDetails(List<BookModel> bokModels) 
  throws RuntimeException {
   RestTemplate template =  restTemplateBuilder.getRestTemplate();
   HttpEntity<?> httpEntity = createHttpEntityForBulkPost(bokModels);
   try {
     ResponseEntity<String> response = template.exchange(baseUrl + "bulkpost",
 					HttpMethod.POST,httpEntity,String.class,id);
     return response.getBody();
   } catch(HttpClientErrorException e) {
     logger.error(e.getMessage(), e);
     throw RuntimeException(e.getMessage());
   }
 }
 
 String createBookDetails(BookModel bookModel)  
  throws RuntimeException {
   RestTemplate template =  restTemplateBuilder.getRestTemplate();
   HttpEntity<?> httpEntity = createHttpEntityForPost(bokModel);
   try {
     ResponseEntity<String> response = template.exchange(baseUrl + "post",
 						HttpMethod.POST,httpEntity,String.class,id);
     return response.getBody();
   } catch(HttpClientErrorException e) {
     logger.error(e.getMessage(), e);
     throw RuntimeException(e.getMessage());
   }
 }
 
 private HttpEntity<?> createHttpEntityForGet() {
   HttpHeaders headers = new  HttpHeaders();
   headers.set("ApiKey", "myapikey");
   HttpEntity<?> entity = new  HttpEntity<>( headers );
   return entity;
 }
 
 private HttpEntity<?> createHttpEntityForBulkPost(bookModels){
   HttpHeaders headers = new  HttpHeaders();
   headers.set("ApiKey", "myapikey");
   HttpEntity<List<BookModel>> entity = new HttpEntity<>(bookModels, headers);
   return entity;
 }
 
 private  HttpEntity<?> createHttpEntityForPost( bookModel ) {
   //here we can configure the headers of http request that will 
   //be sent to bookstore, can set auth headers too
   HttpHeaders headers = new  HttpHeaders();
   headers.set("ApiKey", "myapikey");
   HttpEntity<BookModel> entity = new HttpEntity<>(bookModel, headers);
   return entity;
 }
}

Using the API Client

In a spring boot controller class the BookStoreApiClient interface is auto wired. Spring will automatically detect that the BookStoreApiClientImpl implements the interface and use that object in place of the interface.

@Autowire
private BookStoreApiClient bookStoreApiClient;  

Then in corresponding methods in the controller class we can call the methods of the ApiClient as

bookStoreApiClient.getBookDetails(id);
bookStoreApiClient.createBulkBookDetails(bokModels); 
bookStoreApiClient.createBookDetails(bokModel);   

So, today we have learned how we can use RestTemplate to create an API Client in a spring boot application.



284 views0 comments

Recent Posts

See All