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 示例


原始标题:RestTemplate Post Request with JSON

« 上一篇: FastUtil 使用指南
» 下一篇: Quarkus 入门指南