SpringBoot 3.2.0自定义RestTemplate未发送Content-Length问题
问题场景
将SpringBoot从3.1.5升级到3.2.0后,基于Apache HttpClient 5.2.3构建的自定义忽略SSL证书验证的RestTemplate,在发起POST请求时未自动设置Content-Length头部,导致被调用服务器返回411 Length Required错误。而在3.1.5版本(使用相同版本的HttpClient)中,该头部会被正常设置。
临时可通过以下方式规避:
- POST请求无请求体时,发送空字符串
""替代null - 手动将请求对象序列化为字符串后再发送
原因分析
SpringBoot 3.2.0对RestTemplate的请求处理逻辑做了调整:当请求体为null时,框架不再自动为POST请求添加Content-Length头部;而3.1.5版本中,即使请求体为空,也会默认设置Content-Length为0。这种变化导致依赖该头部的服务器返回411错误——HttpClient本身在处理空请求体时不会主动添加该头部,需要Spring的适配层来处理。
解决方案
方案1:统一替换空请求体为字符串""
在所有无请求体的POST请求中,将请求体参数设为空字符串"",这样HttpMessageConverter会正常序列化并计算内容长度,自动添加Content-Length: 0头部。
示例代码:
// 原写法(会触发问题) restTemplate.postForObject("https://target-api.com/endpoint", null, String.class); // 修改后 restTemplate.postForObject("https://target-api.com/endpoint", "", String.class);
方案2:添加请求拦截器强制设置Content-Length
通过自定义ClientHttpRequestInterceptor,对POST且无请求体的请求手动添加Content-Length头部。
1. 实现拦截器
import org.springframework.http.HttpMethod; import org.springframework.http.HttpRequest; import org.springframework.http.client.ClientHttpRequestExecution; import org.springframework.http.client.ClientHttpRequestInterceptor; import org.springframework.http.client.ClientHttpResponse; import java.io.IOException; public class ContentLengthFixInterceptor implements ClientHttpRequestInterceptor { @Override public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException { // 针对POST请求,且请求体为空的情况,强制设置Content-Length为0 if (HttpMethod.POST.equals(request.getMethod()) && (body == null || body.length == 0)) { request.getHeaders().setContentLength(0); } return execution.execute(request, body); } }
2. 将拦截器添加到RestTemplate
在构建自定义RestTemplate的代码中,添加该拦截器:
RestTemplate restTemplate = new RestTemplate(getHttpClientRequestFactory()); // 添加拦截器 restTemplate.getInterceptors().add(new ContentLengthFixInterceptor());
方案3:自定义HttpMessageConverter处理空请求体
扩展MappingJackson2HttpMessageConverter(或你使用的其他转换器),当请求体为null时,返回空字节数组,并自动触发Content-Length计算。
示例代码:
import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.http.MediaType; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import java.io.IOException; import java.nio.charset.StandardCharsets; public class NullBodyJacksonConverter extends MappingJackson2HttpMessageConverter { public NullBodyJacksonConverter(ObjectMapper objectMapper) { super(objectMapper); } @Override protected byte[] getContent(Object object, MediaType contentType) throws IOException { if (object == null) { return "".getBytes(StandardCharsets.UTF_8); } return super.getContent(object, contentType); } }
然后替换RestTemplate中的默认转换器:
ObjectMapper objectMapper = new ObjectMapper(); NullBodyJacksonConverter converter = new NullBodyJacksonConverter(objectMapper); restTemplate.getMessageConverters().removeIf(c -> c instanceof MappingJackson2HttpMessageConverter); restTemplate.getMessageConverters().add(converter);
方案4:调整目标服务器配置(可选)
如果有权限修改被调用服务器的配置,可以关闭对POST请求Content-Length头部的强制校验,允许无请求体的POST请求。但该方案依赖第三方服务,通常不推荐作为首选。
内容的提问来源于stack exchange,提问作者Hylton Peimer

