RestTemplate 发送 JSON 请求时为何要手动序列化对象?

在使用 RestTemplate 发送 HTTP 请求时,我们经常会遇到这样的问题:直接传递 Java 对象作为请求体可能会导致请求失败,而改为 手动序列化为 JSON 字符串 后,接口才能正常接收。这篇文章将深入解析 为什么 RestTemplate 需要手动序列化 JSON,以及推荐的最佳实践。

1. 直接传递 Java 对象可能导致的问题

通常,我们可能会这样写代码:

HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);

HkRequest request = new HkRequest();
request.setEndDate("2024-03-12");

HttpEntity<HkRequest> entity = new HttpEntity<>(request, headers);

ResponseEntity<BaseResponse<HkData>> response = restTemplate.exchange(
    marketingReturnUrl + "/channel/hk",
    HttpMethod.POST,
    entity,
    new ParameterizedTypeReference<BaseResponse<HkData>>() {}
);

乍一看,这段代码是符合 RestTemplate 规范的,但在某些情况下,它可能会 无法正确序列化 request 对象,从而导致请求失败。主要原因有:

  • RestTemplate 可能解析为 application/x-www-form-urlencoded 而非 application/json
  • HttpMessageConverter 可能无法正确转换对象,特别是当 HkRequest 不是标准的可序列化 POJO 时
  • 如果 HkRequest 内部包含复杂类型(如 DateList 等),Spring 可能无法正确处理

2. 解决方案:手动序列化为 JSON 字符串

为了确保 RestTemplate 发送的请求体是符合 application/json 规范的,我们可以手动将 Java 对象转换为 JSON 字符串:

推荐方式 1:使用 FastJSON 进行手动序列化

HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);

HkRequest request = new HkRequest();
request.setEndDate("2024-03-12");

// 手动序列化对象为 JSON 字符串
HttpEntity<String> entity = new HttpEntity<>(JSON.toJSONString(request), headers);

ResponseEntity<BaseResponse<HkData>> response = restTemplate.exchange(
    marketingReturnUrl + "/channel/hk",
    HttpMethod.POST,
    entity,
    new ParameterizedTypeReference<BaseResponse<HkData>>() {}
);

推荐方式 2:使用 Jackson 进行手动序列化

ObjectMapper objectMapper = new ObjectMapper();
String jsonRequest = objectMapper.writeValueAsString(request);

HttpEntity<String> entity = new HttpEntity<>(jsonRequest, headers);

推荐方式 3:确保 RestTemplate 自动处理 JSON

在某些情况下,如果 RestTemplate 能正确识别 HttpMessageConverter,可以直接使用 Java 对象:

HttpEntity<HkRequest> entity = new HttpEntity<>(request, headers);

⚠️ 但这需要确保 Jackson 依赖存在,并且 HkRequest 是标准的 POJO,否则可能导致解析失败!

3. RestTemplate 默认的 HttpMessageConverter

Spring RestTemplate 默认使用 MappingJackson2HttpMessageConverter(如果 Jackson 依赖存在),但在某些情况下:

  • RestTemplate 可能 未能正确加载 JSON 转换器
  • Spring 可能会 将对象转换为 application/x-www-form-urlencoded,而不是 application/json

手动转换为 JSON 字符串可以 避免这些潜在的问题

4. 总结:最佳实践

方式 适用情况 是否推荐
JSON.toJSONString(request) FastJSON 环境,手动序列化 ✅ 推荐
ObjectMapper.writeValueAsString(request) Jackson 生态,手动序列化 ✅ 推荐
HttpEntity<>(request, headers) 仅当 RestTemplate 默认支持 JSON 序列化 ⚠️ 可能有问题

最佳实践:手动转换 JSON 字符串,避免 RestTemplate 误解析,确保 Content-Typeapplication/json

你可能感兴趣的:(json,java)