1. Overview

In this tutorial, we’ll look at a few examples of using Spring’s RestTemplate in Kotlin. RestTemplate is a synchronous client that’s used to perform HTTP requests. It’s part of the Spring Web module and is used to consume RESTful web services.

2. Project Setup

To begin with, let’s create a Spring Boot project that uses the RestTemplate class to make HTTP requests. Additionally, as a mock service for our tests, we can run this simple REST application.

2.1. Dependencies

First, let’s create a Spring Boot project. We’ll need the Spring Boot Starter Web dependency for this project:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

2.2. Test

To explore the usage of RestTemplate and verify the responses, we can use unit tests.

Let’s create a unit test class and initialize the RestTemplate instance:

class RestTemplateExamplesManualTest {
    private val restTemplate = RestTemplate()
}

Since we’re using the default constructor, the RestTemplate instance will be initialized with the default settings.

2.3. Data Class

We’ll use the /spring-rest/foos endpoint to look at the usage of RestTemplate. Let’s create a Foo data class that will be used as the request body and the response body:

data class Foo(val id: Int, val name: String)

3. Creating a Resource – POST

First, we’ll explore the implementation of POST requests using RestTemplate. We’ll look at different methods available in the RestTemplate class to make POST requests and the differences between them.

3.1. postForObject()

Let’s start with a simple example of creating a resource using the POST method:

@Test
fun `should create a new Foo using postForObject`() {
    val foo = Foo(1, "John")
    val response = restTemplate.postForObject("http://localhost:8082/spring-rest/foos", foo, Foo::class.java)
    assertThat(response).isNotNull
    assertThat(response!!.id).isEqualTo(foo.id)
    assertThat(response.name).isEqualTo(foo.name)
}

We created an instance of the Foo class and passed it to the postForObject() method. This method returns the response body converted to the specified type. Since the API returns the created resource, we can assert that the response is not null and the response body matches the data in the request body.

3.2. postForEntity()

Sometimes, it’s helpful to get the entire response object instead of just the response body. For example, we might want to get the response headers or the status code.

We can use the postForEntity() method to get the entire response object:

@Test
fun `should create a new Foo using postForEntity`() {
    val foo = Foo(1, "John")
    val response = restTemplate.postForEntity("http://localhost:8082/spring-rest/foos", foo, Foo::class.java)
    assertThat(response).isNotNull
    assertThat(response.statusCode).isEqualTo(HttpStatus.CREATED)
    assertThat(response.body).isNotNull
    assertThat(response.body!!.id).isEqualTo(foo.id)
    assertThat(response.body!!.name).isEqualTo(foo.name)
}

3.3. postForLocation()

The postForLocation() method is used to make a POST request and get the URI of the created resource. This is useful when the API returns the URI of the created resource in the Location header instead of the created resource in the response body.

Let’s look at an example:

@Test
fun `should create a new Foo and get its location`() {
    val foo = Foo(1, "John")
    val location = restTemplate.postForLocation("http://localhost:8082/spring-rest/foos", foo)
    assertThat(location).isNotNull
    assertThat(location!!.path).isEqualTo("/spring-rest/foos/1")
}

We created a Foo instance and passed it to the postForLocation() method. This method returns the URI of the created resource. We can use the URI  to retrieve the created resource if needed.

4. Retrieving a Resource – GET

The next step is to retrieve a resource using the GET method. For this purpose, we’ll use the /spring-rest/foos/{id} endpoint.

4.1. getForObject()

The simplest way to retrieve a resource is to use the getForObject() method:

@Test
fun `should get a Foo by id`() {
    var foo = Foo(1, "John")
    restTemplate.postForObject("http://localhost:8082/spring-rest/foos", foo, Foo::class.java)

    foo = restTemplate.getForObject("http://localhost:8082/spring-rest/foos/1", Foo::class.java)!!
    assertThat(foo).isNotNull
    assertThat(foo.id).isEqualTo(1)
    assertThat(foo.name).isEqualTo("John")
}

We passed the URI of the resource and the type of the response body to the getForObject() method. This method returns the response body converted to the specified type.

Similar to POST, we can also use the getForEntity() method to get the entire response object.

4.2. GET With Parameters

In the last example, we hardcoded the ID in the URL. However, in real scenarios, it will be required to keep the parameters dynamic. Let’s look at an example to do that:

@Test
fun `should get a Foo by id using path param`() {
    val foo = restTemplate.getForObject("http://localhost:8082/spring-rest/foos/{id}", Foo::class.java, 1)
    assertThat(foo).isNotNull
    assertThat(foo!!.id).isEqualTo(1)
    assertThat(foo.name).isEqualTo("John")
}

The getForObject() method accepts a vararg of type Any. The values passed in the vararg are used to replace the placeholders in the URI in the order they are passed.

If needed, we can also pass query parameters to the getForObject() method:

@Test
fun `should get a Foo by id using uri variables`() {
    val uriVariables = mapOf("sample" to "value")
    val foo = restTemplate.getForObject("http://localhost:8082/spring-rest/foos/{id}", Foo::class.java, 1, uriVariables)
    assertThat(foo).isNotNull
    assertThat(foo!!.id).isEqualTo(1)
    assertThat(foo.name).isEqualTo("John")
}

Here, we passed a map of query parameters as the last argument to the getForObject() method. The key-value pairs in the map become the query parameters in the URI. For example, the URI in the above example will be /spring-rest/foos/1?sample=value.

5. Updating a Resource – PUT

Next, we’ll look at updating a resource using the PUT method. For this purpose, we’ll use the /spring-rest/foos/{id} endpoint where {id} is the id value of the resource we want to update.

The put() method is used to update a resource:

@Test
fun `should update Foo`() {
    val foo = Foo(1, "John")
    restTemplate.put("http://localhost:8082/spring-rest/foos/{id}", foo, 1)
}

We passed the URI of the resource, the request body, and the path parameters to the put() method. The request body should contain the updated resource.

6. Deleting a Resource – DELETE

Finally, we’ll look at deleting a resource using the DELETE method.

For this, we’ll call the /spring-rest/foos/{id} endpoint using the delete() method:

@Test
fun `should delete Foo`() {
    restTemplate.delete("http://localhost:8082/spring-rest/foos/{id}", 1)
}

7. Other HTTP Methods

Apart from the methods we discussed above, RestTemplate also provides methods for other HTTP methods like HEAD, OPTIONS, and PATCH.

7.1. HEAD Requests

Sometimes, we may only need headers from the server. For this purpose, we can use HEAD requests are used to get only headers from the server. We can use the headForHeaders() method to make a HEAD request:

@Test
fun `should get headers`() {
    val headers = restTemplate.headForHeaders("http://localhost:8082/spring-rest/foos")
    assertThat(headers).isNotNull
    assertThat(headers.contentType).isEqualTo(MediaType.APPLICATION_JSON)
}

Here, we passed the URI of the resource to the headForHeaders() method. This method returns the response headers. Then, we asserted that the Content-Type header is application/json.

7.2. OPTIONS Requests

To get the supported HTTP methods for a resource, we can use OPTIONS requests. We can use the optionsForAllow() method to make an OPTIONS request:

@Test
fun `should get options`() {
    val options = restTemplate.optionsForAllow("http://localhost:8082/spring-rest/foos")
    assertThat(options).isNotNull
    assertThat(options).contains(HttpMethod.GET, HttpMethod.POST, HttpMethod.HEAD, HttpMethod.OPTIONS)
}

Here, we passed the URI of the resource to the optionsForAllow() method. We can then assert that this method returns the supported HTTP methods for the resource.

8. The exchange() Method

In addition to the above methods, RestTemplate also provides the exchange() method. We can use it to make any HTTP request by specifying the HTTP method, a HttpEntity object, and the response type.

The exchange() method is primarily used for sending requests with headers, which is the most common scenario. In this case, the HttpEntity object encompasses both the request headers and the request body.

Let’s look at an example to make a POST request using the exchange() method with headers:

@Test
fun `should create entity using exchange`() {
    val headers = HttpHeaders()
    headers.contentType = MediaType.APPLICATION_JSON
    val foo = Foo(1, "John")
    val request = HttpEntity(foo, headers)
    val response = restTemplate.exchange("http://localhost:8082/spring-rest/foos", HttpMethod.POST, request, Foo::class.java)
    assertThat(response).isNotNull
    assertThat(response.statusCode).isEqualTo(HttpStatus.CREATED)
}

Here, we created a HttpEntity object with the request body and headers. Then, we passed the HttpEntity object to the exchange() method along with the URI of the resource and the HTTP method.

The exchange() method returns a ResponseEntity object. We can then assert that the status code of the response is CREATED.

9. Customizing the RestTemplate

In the above examples, we used the default RestTemplate instance. However, we can also customize the RestTemplate instance to suit our needs.

One of the common use cases is to add a timeout to the RestTemplate instance. Let’s look at an example to do that:

class RestTemplateConfig {
    @Bean("restTemplateCustom")
    fun restTemplate(): RestTemplate {
        val restTemplate = RestTemplate()
        val requestFactory = HttpComponentsClientHttpRequestFactory()
        requestFactory.setConnectTimeout(5000)
        requestFactory.setReadTimeout(5000)
        restTemplate.requestFactory = requestFactory;

        return restTemplate;
    }
}

We first created a default RestTemplate instance. Then we created a HttpComponentsClientHttpRequestFactory instance and set it as the request factory for the RestTemplate instance. This also sets the implementation of the RestTemplate to Apache HTTPClient.

We also set the connect and read timeouts to 5 seconds in the factory.

Finally, we can auto-wire the RestTemplate instance in our test class:

class CustomRestTemplateExamplesManualTest {
    @Autowired
    @Qualifier("restTemplateCustom")
    lateinit var restTemplate: RestTemplate
}

We can then replace the default constructor call we used in the above examples with the autowired RestTemplate instance.

9.1. PATCH Request Using Custom RestTemplate

We can use PATCH requests to update a part of the resource. The default RestTemplate instance doesn’t support PATCH requests. However, we can use our custom RestTemplate instance to make PATCH requests since it uses the Apache HTTP client under the hood.

We can use the patchForObject() method to make a PATCH request:

@Test
fun `should update the name of Foo`() {
    var foo = Foo(1, "John")
    restTemplate.postForObject("http://localhost:8082/spring-rest/foos", foo, Foo::class.java)

    val update = mapOf("name" to "Jane")
    foo = restTemplate.patchForObject("http://localhost:8082/spring-rest/foos/{id}", update, Foo::class.java, 1)!!
    Assertions.assertThat(foo).isNotNull
    Assertions.assertThat(foo.name).isEqualTo("Jane")
}

In the above code, we passed the URI of the resource, the request body, and the path parameters to the patchForObject() method. The request body should contain a map with the updated part of the resource. In this case, the map contains the name of the resource.

The response contains the full updated resource and not just the updated part. We can then assert that the name of the resource is updated.

10. Conclusion

In this article, we looked at the different methods provided by the RestTemplate class to make HTTP requests in Kotlin. We also looked at how to customize the RestTemplate instance to suit our needs.

As always, the complete source code for the examples is available over on GitHub.