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

posted @ 2025-05-22 15:23  Dyhuang  阅读(706)  评论(0)    收藏  举报