Spring Boot升级到3.2.5后RestTemplate空请求丢失Content-Length致411错误
问题描述
将Spring Boot从3.1.x升级到3.2.5后,RestTemplate发送空请求体的POST请求时出现以下异常行为:
- 不再自动添加
Content-Length: 0请求头 - 手动显式添加该请求头也会被RestTemplate丢弃
最终导致目标服务器返回HTTP 411 Length Required错误。
升级前后请求日志对比(日志来自DEBUG级别的o.a.h.c.h.i.Wire.wire(92) - http-outgoing-0):
Spring Boot 3.1.10 请求日志
---- >> "POST /oauth2/token HTTP/1.1[\r][\n]" ---- >> "Accept: text/plain, application/json, application/*+json, */*[\r][\n]" ---- >> "Authorization: Basic ******=[\r][\n]" ---- >> "grant_type: client_credentials[\r][\n]" ---- >> "scope: you don't need this[\r][\n]" ---- >> "Content-Type: application/x-www-form-urlencoded[\r][\n]" ---- >> "Accept-Encoding: gzip, x-gzip, deflate[\r][\n]" ---- >> "Content-Length: 0[\r][\n]" ---- >> "Host: this one too[\r][\n]" ---- >> "Connection: keep-alive[\r][\n]" ---- >> "User-Agent: Apache-HttpClient/5.2.1 (Java/17.0.10)[\r][\n]" ---- >> "[\r][\n]"
Spring Boot 3.2.5 请求日志
---- >> "POST /oauth2/token HTTP/1.1[\r][\n]" ---- >> "Accept: text/plain, application/json, application/*+json, */*[\r][\n]" ---- >> "Authorization: Basic ****=[\r][\n]" ---- >> "grant_type: client_credentials[\r][\n]" ---- >> "scope: you don't need this[\r][\n]" ---- >> "Content-Type: application/x-www-form-urlencoded[\r][\n]" ---- >> "Accept-Encoding: gzip, x-gzip, deflate[\r][\n]" ---- >> "Host: this one too[\r][\n]" ---- >> "Connection: keep-alive[\r][\n]" ---- >> "User-Agent: Apache-HttpClient/5.2.1 (Java/17.0.10)[\r][\n]"
解决方案
1. 临时快速修复:使用空字符串作为请求体
将原本的空请求体替换为空字符串"",RestTemplate会自动为其添加Content-Length: 0头,简单直接:
restTemplate.postForObject(url, "", YourResponse.class);
2. 自定义拦截器强制添加Content-Length头
实现ClientHttpRequestInterceptor,在请求发送前检查空体并手动添加头:
public class EmptyBodyContentLengthInterceptor implements ClientHttpRequestInterceptor { @Override public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException { if (body.length == 0 && !request.getHeaders().containsKey(HttpHeaders.CONTENT_LENGTH)) { request.getHeaders().setContentLength(0); } return execution.execute(request, body); } }
注册到RestTemplate:
restTemplate.getInterceptors().add(new EmptyBodyContentLengthInterceptor());
3. 调整FormHttpMessageConverter的空体处理
针对application/x-www-form-urlencoded类型的请求,修改转换器配置确保空表单生成Content-Length: 0:
restTemplate.getMessageConverters().stream() .filter(c -> c instanceof FormHttpMessageConverter) .findFirst() .ifPresent(c -> ((FormHttpMessageConverter) c).setWriteEmptyForm(true));
4. 配置底层Apache HttpClient行为
如果RestTemplate使用Apache HttpClient作为请求工厂,通过拦截器强制为空体请求添加头:
CloseableHttpClient httpClient = HttpClientBuilder.create() .addInterceptorFirst((HttpRequestInterceptor) (request, context) -> { if (request instanceof HttpEntityEnclosingRequest entityRequest) { if (entityRequest.getEntity() == null || entityRequest.getEntity().getContentLength() == -1) { entityRequest.setHeader(HttpHeaders.CONTENT_LENGTH, "0"); } } }) .build(); restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory(httpClient));
原因说明
该行为变化源于Spring Boot 3.2对应的Spring Framework 6.1版本中,RestTemplate底层请求处理逻辑的调整,尤其是针对空请求体的Content-Length头生成规则。Apache HttpClient 5.x的相关行为变更也可能影响了最终结果,该调整未在Spring Boot 3.2的发布说明中单独列出。
内容的提问来源于stack exchange,提问作者Arun Gowda
相关产品推荐
相关产品推荐

