1. 简介
在本文中,我们将通过示例讲解如何使用 Spring 提供的 RestTemplate
来发送携带 JSON 数据的 POST 请求。适用于有经验的 Java 开发者,快速掌握 RestTemplate 的常见用法和注意事项。
2. 示例准备
首先,我们定义一个简单的 Person
类,用于表示要传输的数据:
public class Person {
private Integer id;
private String name;
// 构造方法、getter、setter
}
接着定义一个服务接口 PersonService
及其简单实现,用于处理业务逻辑:
public interface PersonService {
Person saveUpdatePerson(Person person);
Person findPersonById(Integer id);
}
实现类我们使用一个 dummy 实现,不做实际逻辑处理,仅用于测试 Web 层。
3. REST API 设计
我们为 Person
类设计两个简单的接口:
@PostMapping(
value = "/createPerson", consumes = "application/json", produces = "application/json")
public Person createPerson(@RequestBody Person person) {
return personService.saveUpdatePerson(person);
}
@PostMapping(
value = "/updatePerson", consumes = "application/json", produces = "application/json")
public Person updatePerson(@RequestBody Person person, HttpServletResponse response) {
response.setHeader("Location", ServletUriComponentsBuilder.fromCurrentContextPath()
.path("/findPerson/" + person.getId()).toUriString());
return personService.saveUpdatePerson(person);
}
✅ 要点说明:
- 使用
@PostMapping
时,consumes = "application/json"
表示接收 JSON 格式请求体 produces = "application/json"
表示返回 JSON 格式的响应@RequestBody
注解用于将请求体绑定到 Java 对象- 控制器类使用
@RestController
注解,自动为每个方法添加@ResponseBody
4. 使用 RestTemplate 发送请求
我们通过单元测试来验证 RestTemplate
的使用方式。首先初始化一些公共变量:
@BeforeClass
public static void runBeforeAllTestMethods() {
createPersonUrl = "http://localhost:8080/spring-rest/createPerson";
updatePersonUrl = "http://localhost:8080/spring-rest/updatePerson";
restTemplate = new RestTemplate();
headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
personJsonObject = new JSONObject();
personJsonObject.put("id", 1);
personJsonObject.put("name", "John");
}
此外,我们还需要一个 ObjectMapper
来处理 JSON 数据转换:
private final ObjectMapper objectMapper = new ObjectMapper();
⚠️ 注意:请求头中一定要设置 Content-Type: application/json
,否则 Spring 不会识别 JSON 请求体。
4.1. 使用 postForObject
postForObject
方法会发送 POST 请求并返回转换后的对象。
@Test
public void givenDataIsJson_whenDataIsPostedByPostForObject_thenResponseBodyIsNotNull()
throws IOException {
HttpEntity<String> request =
new HttpEntity<String>(personJsonObject.toString(), headers);
String personResultAsJsonStr =
restTemplate.postForObject(createPersonUrl, request, String.class);
JsonNode root = objectMapper.readTree(personResultAsJsonStr);
assertNotNull(personResultAsJsonStr);
assertNotNull(root);
assertNotNull(root.path("name").asText());
}
也可以直接返回 Person
对象:
Person person = restTemplate.postForObject(createPersonUrl, request, Person.class);
assertNotNull(person);
assertNotNull(person.getName());
4.2. 使用 postForEntity
postForEntity
返回的是一个 ResponseEntity
,包含完整的响应信息(如状态码、响应头等)。
@Test
public void givenDataIsJson_whenDataIsPostedByPostForEntity_thenResponseBodyIsNotNull()
throws IOException {
HttpEntity<String> request =
new HttpEntity<String>(personJsonObject.toString(), headers);
ResponseEntity<String> responseEntityStr = restTemplate.
postForEntity(createPersonUrl, request, String.class);
JsonNode root = objectMapper.readTree(responseEntityStr.getBody());
assertNotNull(responseEntityStr.getBody());
assertNotNull(root.path("name").asText());
}
同样可以返回 Person
类型:
ResponseEntity<Person> responseEntityPerson = restTemplate.
postForEntity(createPersonUrl, request, Person.class);
assertNotNull(responseEntityPerson.getBody());
assertNotNull(responseEntityPerson.getBody().getName());
4.3. 使用 postForLocation
postForLocation
用于发送 POST 请求,并返回响应头中的 Location
字段,适用于创建资源后返回地址的场景。
@Test
public void givenDataIsJson_whenDataIsPostedByPostForLocation_thenResponseBodyIsTheLocationHeader()
throws JsonProcessingException {
HttpEntity<String> request = new HttpEntity<String>(personJsonObject.toString(), headers);
URI locationHeader = restTemplate.postForLocation(updatePersonUrl, request);
assertNotNull(locationHeader);
}
5. 处理 JSON 请求中的字符编码问题
Spring Boot 默认使用 UTF-8 编码处理请求,但如果你在配置中设置了 server.servlet.encoding.charset=ISO-8859-1
,则可能会遇到乱码问题。
5.1. 模拟乱码场景
启动应用时设置编码为 ISO-8859-1:
SpringApplication app = new SpringApplication(RestTemplateApplication.class);
app.setDefaultProperties(
Collections.singletonMap("server.servlet.encoding.charset", "ISO-8859-1"));
app.run(args);
构造一个含日文字符的请求对象:
Person japanese = new Person(100, "関連当");
发送请求:
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<Person> request = new HttpEntity<>(japanese, headers);
Person person = restTemplate.postForObject(createPersonUrl, request, Person.class);
验证失败:
assertNotEquals("関連当", person.getName());
⚠️ 原因:ISO-8859-1 不支持日文字符,导致乱码。
5.2. 解决方法
显式设置请求头中的 Content-Type
包含 UTF-8 编码:
headers.set("Content-type", "application/json;charset=UTF-8");
重新发送请求并验证:
Person person = restTemplate.postForObject(createPersonUrl, request, Person.class);
assertEquals("関連当", person.getName());
✅ 成功解决乱码问题!
6. 小结
本文通过多个示例演示了如何使用 RestTemplate
发送 JSON 格式的 POST 请求,并展示了三种常用方法:
方法名 | 返回类型 | 用途 |
---|---|---|
postForObject |
T |
直接返回转换后的对象 |
postForEntity |
ResponseEntity<T> |
返回完整响应信息 |
postForLocation |
URI |
返回 Location 响应头 |
此外,还介绍了处理字符编码乱码的解决方案,适用于国际化场景。
示例代码已整理上传至 GitHub,欢迎查看:RestTemplate JSON POST 示例 ✅