RestTemplate in Spring Boot

RestTemplate in Spring Boot | RestTemplate class is used to make an HTTP call to any web service Application (java and non-java apps) that reads the final response into ResponseEntity or a direct time. It supports auto-type conversion of Input/Output into GlobalFormat (i.e. Object to JSON/XML and JSON/XML to Object).

  • We can read even header information, body, status code… etc.
  • RestTemplate needs major input i.e. URL.
  • All Endpoint details must be given finally. The endpoint contains the URL, HTTP Method, Input, Output, and SupportedMedia Type details.
  • Details required to make an HTTP call to a service called Endpoint details.
  • RestTemplate supports all HTTP Method Types (GET, POST, … etc).
  • RestTemplate supports all HTTP Method Types (GET, POST, … etc).

Use case:- Movie Booking application (Consumer) and Bank application (Producer).

RestTemplate Example in Spring Boot

Create a Spring Boot Project “RestProducer” with the following dependencies:- Lombok, Spring web, Spring Boot DevTools.

Create another Spring Boot project “RestConsumer” with the following dependencies:- Lombok, Spring web.

Assume that both projects are two different servers.

RestProducer Code

package com.knowprogram.demo.rest;

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/std")
public class StudentRestController {

    @GetMapping("/data")
    public ResponseEntity<String> showMsg() {
        return ResponseEntity.ok("Hello");
    }
}

RestConsumer Code

In application.properties:-

server.port=9898

AppConfig: Spring Boot never provides Auto-Configuration for RestTemplate, we should configure it manually when we are writing consumer application.

package com.knowprogram.demo.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@Configuration
public class AppConfig {
    
    @Bean
    public RestTemplate rt() {
        return new RestTemplate();
    }
}
package com.knowprogram.demo.runner;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

@Component
public class RestConsumerOne implements CommandLineRunner {

    private static final Logger log = LoggerFactory.getLogger(RestConsumerOne.class);

    @Autowired
    private RestTemplate rt;

    @Override
    public void run(String... args) throws Exception {
        // 1. define URL of provider
        String url = "http://localhost:8080/std/data";

        // 2. Make call and get response
        ResponseEntity<String> resp = rt.getForEntity(url, String.class);

        // 3. Print details
        log.info("Status ID {} ", resp.getStatusCode().value());
        log.info("Status CODE {} ", resp.getStatusCode());
        log.info("Response Body {}", resp.getBody());
        log.info("Response Header {}", resp.getHeaders());
        
        // 4. stop server manually
        System.exit(0);
    }

}

First, run the RestProducer application, then run the RestConsumer application. Output for RestConsumer:-

mc.k.demo.runner.RestConsumerOne         Status ID 200 
mc.k.demo.runner.RestConsumerOne         Status CODE 200 OK 
mc.k.demo.runner.RestConsumerOne         Response Body Hello
mc.k.demo.runner.RestConsumerOne         Response Header [Content-Type:"text/plain;charset=UTF-8", Content-Length:"5", Date:"Tue, 25 Jun 2024 12:09:08 GMT", Keep-Alive:"timeout=60", Connection:"keep-alive"]

In the above application:-

  • ResponseEntity<T> getForEntity(String url, Class<T> class):- This method is given by RestTemplate(C) used to make HTTP calls using GET type, takes two inputs:- URL, Expected Response Type.
  • The string can hold any type of data like int, Double, boolean, JSON, XML…etc)
  • The above method returns data in ResponseEntity<T> that holds all response information.
  • System.exit(0) used to stop main thread/Server.
  • Do not use the same port number for the Producer/Consumer App. Consumer App: server.port=9898
  • By using the method getForObject(URL, classType) we can get only ResponseBody, not other details.
String body = rt.getForObject(url, String.class);
log.info("Response Body {}", body);

RestTemplate POST Example

postForEntity(url, httpEntity, responseType, pathVariables) that returns ResponseEntity.
HttpEntity = HttpHeaders + Body (JSON/XML) it is also called as request entity while making call using POST/PUT method.

In RestProducer:-

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
    private Integer stdId;
    private String stdName;
    private Double stdFee;
}
@RestController
@RequestMapping("/std")
public class StudentRestController {

    @GetMapping("/data")
    public ResponseEntity<String> showMsg() {
        return ResponseEntity.ok("Hello");
    }
    
    @PostMapping("/create")
    public ResponseEntity<String> createStudent(@RequestBody Student student ) {
        return ResponseEntity.ok("Student data is " + student);
    }
}

In RestConsumer, create another class:-

package com.knowprogram.demo.runner;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

@Component
public class RestConsumerPostType implements CommandLineRunner {

    private static final Logger log = LoggerFactory.getLogger(RestConsumerPostType.class);

    @Autowired
    private RestTemplate rt;

    @Override
    public void run(String... args) throws Exception {
        // 1. define URL
        String url = "http://localhost:8080/std/create";

        // 2. HttpEntity = header + body
        String body = "{\"stdId\":100,\"stdName\":\"A\",\"stdFee\":300.0}";
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        HttpEntity<String> request = new HttpEntity<>(body, headers);

        // 3. make request and get response
        // URL, HttpEntity, ResponseType, PathVariable (optional)
        ResponseEntity<String> resp = rt.postForEntity(url, request, String.class);

        // 4. Print details
        log.info("Status ID {} ", resp.getStatusCode().value());
        log.info("Status CODE {} ", resp.getStatusCode());
        log.info("Response Body {}", resp.getBody());
        log.info("Response Header {}", resp.getHeaders());

        // 5. stop server manually
        System.exit(0);
    }
}

RestTemplate exchange() Method

RestTemplate has given one global method exchange() used to make any type of HTTP call (GET/POST/PUT/DELETE) that finally returns ResponseEntity<T>.

ResponseEntity<T> exchange(url, HttpMethod, HttpEntity, ResponseType, pathVatiables)
  • HttpMethod:- It is an enum having values like GET, POST, PUT, and DELETE.
  • HttpEntity = request Body + request Headers. It can be null for the GET type (No Header and Body case).
  • ResponseType can be String, ClassType, and Collection/Array.

Compared to postForEntity(), and getForEntity() methods, exchange() method is most commonly used because it is able to handle all type of HttpMethod.

In RestConsumer application:-

package com.knowprogram.demo.runner;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

@Component
public class RestConsumerExchangePost implements CommandLineRunner {

    private static final Logger log = 
        LoggerFactory.getLogger(RestConsumerExchangePost.class);

    @Autowired
    private RestTemplate rt;

    @Override
    public void run(String... args) throws Exception {

        // 1. define URL
        String url = "http://localhost:8080/std/create";

        // 2. HttpEntity = header + body
        String body = "{\"stdId\":100,\"stdName\":\"A\",\"stdFee\":300.0}";
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        HttpEntity<String> requestEntity = new HttpEntity<>(body, headers);

        // 3. make request and get response
        // URL, HttpMethod, HttpEntity, ResponseType, PathVariable (optional)
        ResponseEntity<String> resp = 
           rt.exchange(url, HttpMethod.POST, requestEntity, String.class);

        // 4. Print details
        log.info("Status ID {} ", resp.getStatusCode().value());
        log.info("Status CODE {} ", resp.getStatusCode());
        log.info("Response Body {}", resp.getBody());
        log.info("Response Header {}", resp.getHeaders());

        // 5. stop server manually
        System.exit(0);

    }
}

PUT and DELETE Examples

The put() and delete() methods makes HTTP call without any issues, but no response is taken back to consumer app. i.e. returns void type. There it is better to use exchange() method instead of put() and delete() methods.

In RestProducer:-

@RestController
@RequestMapping("/std")
public class StudentRestController {

    @PutMapping("/modify")
    public ResponseEntity<String> updateStudent(@RequestBody Student student) {
        System.out.println("EXECUTED.........");
        return ResponseEntity.ok("Student data is " + student);
    }

    @DeleteMapping("/remove/{id}")
    public ResponseEntity<String> removeStudent(@PathVariable Integer id) {
        return ResponseEntity.ok("Student removed " + id);
    }
}

In RestConsumer:-

package com.knowprogram.demo.runner;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

@Component
public class RestConsumerExchangePut implements CommandLineRunner {

    private static final Logger log = 
          LoggerFactory.getLogger(RestConsumerExchangePut.class);

    @Autowired
    private RestTemplate rt;

    @Override
    public void run(String... args) throws Exception {

        // 1. define URL
        String url = "http://localhost:8080/std/modify";

        // 2. create HttpEntity = header + body
        HttpHeaders headers = new HttpHeaders();
        // valid JSON ({}:- used to create object with default value)
        String body = "{}"; 
        headers.setContentType(MediaType.APPLICATION_JSON);
        HttpEntity<String> requestEntity = new HttpEntity<>(body, headers);

        // 3. make request and get response
        // exchange(URL, HttpMethod, HttpEntity, ResponseType, PathVariable (optional))
        ResponseEntity<String> resp = 
                 rt.exchange(url, HttpMethod.PUT, requestEntity, String.class);

        // 4. Print details
        log.info("Status ID {} ", resp.getStatusCode().value());
        log.info("Status CODE {} ", resp.getStatusCode());
        log.info("Response Body {}", resp.getBody());
        log.info("Response Header {}", resp.getHeaders());

        // 5. stop server manually
        System.exit(0);

    }
}
package com.knowprogram.demo.runner;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

@Component
public class RestConsumerExchangeDelete implements CommandLineRunner {

    private static final Logger log = 
          LoggerFactory.getLogger(RestConsumerExchangeDelete.class);

    @Autowired
    private RestTemplate rt;

    @Override
    public void run(String... args) throws Exception {

        // 1. define URL
        String url = "http://localhost:8080/std/remove/{id}";

        // 2. create HttpEntity = header + body
        // no header + body

        // 3. make request and get response
        // exchange(URL, HttpMethod, HttpEntity, ResponseType, PathVariable (optional))
        ResponseEntity<String> resp = 
                 rt.exchange(url, HttpMethod.DELETE, null, String.class, 101);

        // 4. Print details
        log.info("Status ID {} ", resp.getStatusCode().value());
        log.info("Status CODE {} ", resp.getStatusCode());
        log.info("Response Body {}", resp.getBody());
        log.info("Response Header {}", resp.getHeaders());

        // 5. stop server manually
        System.exit(0);

    }
}

We can pass any number of uriVariables (@Pathvariable) as comma separated value in exchange() method.

Auto-Type Conversion Support for ResponseType(Class<T>)

If the producer returns String type output then consumer can read same into String object. Else if Producer returns complex Type Class, List/Set then even consumer methods getForEntity(), postForEntity(), exchange() methods support auto-type conversion of response data i.e. JSON –> ClassType/Array.

Ex#1
Producer: ResponseEntity<String>
Consumer: getForEntity(url, String.class)

Ex#2
Producer: ResponseEntity<Employee>
Consumer: getForEntity(url, Employee.class) // JSON –> Employee
We can read plain JSON getForEntity(url, String.class) // print as it is JSON

Ex#3
Producer: ResponseEntity<List<Employee>>
Consumer: getForEntity(url, Employee[].class) // JSON -> Employee Array
We can read plain JSON getForEntity(url, String.class) // print as it is JSON

In RestProducer:-

@RestController
@RequestMapping("/std")
public class StudentRestController {
    @GetMapping("/one/{id}")
    public ResponseEntity<Student> getOneStudent(@PathVariable Integer id) {
        return ResponseEntity.ok(new Student(id, "Test", 200.0));
    }
}

In RestConsumer, create the same Student model class:-

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
    private Integer stdId;
    private String stdName;
    private Double stdFee;
}
package com.knowprogram.demo.runner;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

@Component
public class RestConsumerGetClassType implements CommandLineRunner {

    private static final Logger log = 
        LoggerFactory.getLogger(RestConsumerGetClassType.class);

    @Autowired
    private RestTemplate rt;

    @Override
    public void run(String... args) throws Exception {

        // 1. define URL
        String url = "http://localhost:8080/std/one/{id}";

        // 2. make request and get response
        // exchange(URL, HttpMethod, HttpEntity, ResponseType, PathVariable (optional))
        ResponseEntity<Student> resp = 
              rt.exchange(url, HttpMethod.GET, null, Student.class, 101);

        // 3. Print details
        log.info("Status ID {} ", resp.getStatusCode().value());
        log.info("Status CODE {} ", resp.getStatusCode());
        log.info("Response Body {}", resp.getBody());
        log.info("Response Header {}", resp.getHeaders());

        // 4. stop server manually
        System.exit(0);

    }
}

Another example with List

In RestProducer:-

@RestController
@RequestMapping("/std")
public class StudentRestController {
    @GetMapping("/all")
    public ResponseEntity<List<Student>> getAllStudent() {
        return ResponseEntity.ok(
            Arrays.asList(
                new Student(110, "A", 200.0),
                new Student(111, "B", 300.0),
                new Student(112, "C", 400.0)
            )
        );
    }
}

In RestProducer:-

package com.knowprogram.demo.runner;

import java.util.Arrays;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

@Component
public class RestConsumerGetClassTypeAll implements CommandLineRunner {

    private static final Logger log = 
             LoggerFactory.getLogger(RestConsumerGetClassTypeAll.class);

    @Autowired
    private RestTemplate rt;

    @Override
    public void run(String... args) throws Exception {

        // 1. define URL
        String url = "http://localhost:8080/std/all";

        // 2. make request and get response
        // exchange(URL, HttpMethod, HttpEntity, ResponseType, PathVariable (optional))
        ResponseEntity<Student[]> resp = 
              rt.exchange(url, HttpMethod.GET, null, Student[].class);

        // 3. Print details
        log.info("Status ID {} ", resp.getStatusCode().value());
        log.info("Status CODE {} ", resp.getStatusCode());
        log.info("Response Body {}", Arrays.asList(resp.getBody()));
        log.info("Response Header {}", resp.getHeaders());

        // 4. stop server manually
        System.exit(0);

    }
}

If you enjoyed this post, share it with your friends. Do you want to share more information about the topic discussed above or do you find anything incorrect? Let us know in the comments. Thank you!

Leave a Comment

Your email address will not be published. Required fields are marked *