1. 概述

在本文中,我们将通过几个示例来介绍在 Kotlin 中使用 Spring 提供的 RestTemplateRestTemplate 是一个同步客户端,用于发起 HTTP 请求,是 Spring Web 模块的一部分,常用于调用 RESTful 接口。

2. 项目搭建

我们首先创建一个 Spring Boot 项目,使用 RestTemplate 来发起 HTTP 请求。为了方便测试,我们使用一个本地的模拟服务:spring-resttemplate 示例项目

2.1. 依赖配置

首先,我们需要引入 Spring Boot Web 模块依赖:

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

2.2. 测试类初始化

接下来,我们创建一个测试类,并初始化 RestTemplate 实例:

class RestTemplateExamplesManualTest {
    private val restTemplate = RestTemplate()
}

该实例使用默认构造函数创建,使用默认配置。

2.3. 数据类定义

我们将使用 /spring-rest/foos 接口进行测试,为此定义一个 Foo 数据类:

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

用于作为请求体和响应体的类型。

3. 创建资源 – POST

我们先来看 RestTemplate 如何发起 POST 请求。Spring 提供了多个方法来实现这一功能。

3.1. postForObject()

使用 postForObject() 方法发送 POST 请求并获取响应体:

@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)
}

✅ 适用于只需要响应体的场景。

3.2. postForEntity()

如果需要获取完整的响应对象(包括状态码和头信息),可以使用 postForEntity()

@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()

如果接口返回的是资源的 URI(如 Location 头),可以使用 postForLocation() 获取:

@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")
}

✅ 适用于获取新资源 URI 的场景。

4. 获取资源 – GET

接下来,我们演示如何使用 RestTemplate 发起 GET 请求获取资源。

4.1. getForObject()

最简单的 GET 请求方式:

@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")
}

✅ 适用于仅需获取响应体的场景。

4.2. GET 请求带参数

可以使用路径参数或查询参数:

@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")
}

也可以传入 Map 作为查询参数:

@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")
}

生成的 URL 为:/spring-rest/foos/1?sample=value

5. 更新资源 – PUT

使用 put() 方法更新资源:

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

⚠️ 注意:PUT 是全量更新,请求体应包含完整资源。

6. 删除资源 – DELETE

使用 delete() 方法删除资源:

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

7. 其他 HTTP 方法

RestTemplate 也支持其他 HTTP 方法如 HEAD、OPTIONS、PATCH 等。

7.1. HEAD 请求

获取响应头信息:

@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)
}

7.2. OPTIONS 请求

获取接口支持的 HTTP 方法:

@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)
}

8. exchange() 方法

exchange() 是一个通用方法,可以指定任意 HTTP 方法、请求头、请求体等:

@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)
}

✅ 适用于需要自定义请求头的场景。

9. 自定义 RestTemplate

默认的 RestTemplate 实例可以满足基本需求,但有时需要自定义,例如设置超时。

9.1. 自定义配置示例

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

通过设置 HttpComponentsClientHttpRequestFactory,我们可以使用 Apache HttpClient 实现,并设置连接和读取超时。

9.2. 使用自定义 RestTemplate

注入并使用:

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

9.3. PATCH 请求支持

默认 RestTemplate 不支持 PATCH 方法,但自定义后可以使用:

@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")
}

✅ PATCH 用于部分更新,适合只修改部分字段的场景。

10. 总结

本文介绍了 Kotlin 中使用 RestTemplate 的各种方法,包括:

  • POST:创建资源
  • GET:获取资源
  • PUT:全量更新
  • DELETE:删除资源
  • HEAD、OPTIONS、PATCH 等其他方法
  • 使用 exchange() 进行通用请求
  • 自定义 RestTemplate(如设置超时)

所有完整示例代码都可以在 GitHub 上找到。


原始标题:RestTemplate Examples in Kotlin