1. 概述
在本文中,我们将通过几个示例来介绍在 Kotlin 中使用 Spring 提供的 RestTemplate
。RestTemplate
是一个同步客户端,用于发起 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 上找到。