SpringBoot 使用RestTemplate 返回 401 [no body] 无法读取响应体(response body)的原因分析
前言
这个问题浪费了我半天的调试时间,所以记录下。
问题描述
在springboot(3.4.1)中使用RestTemplate远程请求(post)一个带有身份验证的api,发现当http的状态码为200的时候可以正常的读取response body,但如果响应的是401的错误码,则无法读取到response body 提示[no body]
代码如下
@Test
public void restTemplateDefaultTest() {
try {
RestTemplate restTemplate = new RestTemplate();
// 创建 HttpHeaders
HttpHeaders headers = new HttpHeaders();
headers.set("Content-Type", "application/json");
String bodyStr = "{\"clientId\":\"ZBcxlXBHXWwViJS4\",\"grantType\":\"authorization_code\",\"code\":\"1232\"}";
// 创建 HttpEntity 实例,包含 headers
HttpEntity<String> requestEntity = new HttpEntity<>(bodyStr, headers);
ResponseEntity<String> response = restTemplate.exchange(requestUrl, HttpMethod.POST, requestEntity, String.class);
// 处理响应
if (response.getStatusCode().is2xxSuccessful()) {
String body = response.getBody();
System.out.println("响应状态码: " + response.getStatusCode());
System.out.println("响应体: " + body);
} else {
System.out.println("请求失败,状态码: " + response.getStatusCode());
}
} catch (HttpClientErrorException ex){
System.out.println("请求失败,返回的body: " + ex.getResponseBodyAsString());
ex.printStackTrace();
}
}
执行后发现我已经给请求加上了try-catch了,但如果服务端响应401的状态尽管可以捕获到错误,但无法输出body体,错误截图如下

但其实通过postman调用,真实是存在响应的。
{
"code": 90002,
"message": "authorization code is invalid",
"timestamp": 1747892225652,
"data": null
}
所以到底是什么原因导致无法读取到响应体呢?
问题原因
SpringBoot 默认的RestTemplate使用的客户端是SimpleClientHttpRequestFactory
而SimpleClientHttpRequestFactory 类的实现是基于Java的HttpURLConnection
Java 原生的 HttpURLConnection 的行为有一些坑:
- 如果服务器返回 400 以上的状态码时,调用
getInputStream()会抛出IOException。 - 必须用
getErrorStream()获取响应体,但是:- 有些情况下
getErrorStream()是null(比如服务端没写 body)。 - 并且
HttpURLConnection在某些特定模式(如 chunked、streaming mode)下,错误码下会直接抛出异常,而不提供 errorStream。
- 有些情况下
- 导致 RestTemplate 封装时对 400+ 的响应无法可靠获取 body。
解决方案
采用Apache的Http客户端组件Apache HttpClient
HttpComponentsClientHttpRequestFactory 是基于 Apache HttpClient
- Apache HttpClient 是一个更强大、健壮的 HTTP 客户端库,它不会因为状态码 >= 400 而抛异常。
- 它始终可以从响应中获取到完整的
HttpEntity,即使是 401、403、500 等错误码。 - Spring 的
RestTemplate通过它能稳定地读取响应体(无论成功或失败)。
配置如下即可:
RestTemplate restTemplate = new RestTemplate(new HttpComponentsClientHttpRequestFactory());
总结对比
| 特性 | SimpleClientHttpRequestFactory (HttpURLConnection) |
HttpComponentsClientHttpRequestFactory (Apache HttpClient) |
|---|---|---|
| 状态码 >= 400 | 抛出异常、getInputStream 失败 | 不抛异常,可以读取响应体 |
| errorStream | 可能为 null | 不需要依赖,HttpClient 直接返回 body |
| 可靠性 | 低,依赖实现细节 | 高,健壮、行为一致 |
| 是否推荐 | 不适合需要处理错误响应的场景 | 推荐使用 |
参考资料
https://stackoverflow.com/questions/78317350/spring-resttemplates-401-response-redacted-contains-no-body-even-though-it
https://github.com/spring-projects/spring-boot/issues/40359
https://github.com/spring-projects/spring-framework/issues/14004

浙公网安备 33010602011771号