当响应码为401时ClientHttpResponse.getBody()抛出ResourceAccessException问题
解决RestTemplate 401状态下无法读取响应体的问题
这个问题我之前也碰到过,核心原因是RestTemplate默认的错误处理机制在遇到4xx/5xx这类异常状态码时,会直接抛出HttpClientErrorException(或子类),打断了你读取响应体的流程——哪怕你配置了BufferingClientHttpRequestFactory也没用,因为异常触发得太早了。
下面给你几个可行的解决方案:
方案1:自定义错误处理器,跳过默认异常抛出
最简单的方式是实现一个自定义的ResponseErrorHandler,让它不把401视为“错误”,这样就能正常读取响应体了。
自定义错误处理器代码
import org.springframework.http.client.ClientHttpResponse; import org.springframework.web.client.ResponseErrorHandler; import java.io.IOException; public class NoOpErrorHandler implements ResponseErrorHandler { @Override public boolean hasError(ClientHttpResponse response) throws IOException { // 返回false表示所有状态码都不视为错误,不会触发异常 return false; } @Override public void handleError(ClientHttpResponse response) throws IOException { // 空实现,不做任何错误处理 } }
配置到RestTemplate
RestTemplate restTemplate = new RestTemplate(); // 配置支持重复读取响应的请求工厂 ClientHttpRequestFactory requestFactory = new BufferingClientHttpRequestFactory(new SimpleClientHttpRequestFactory()); restTemplate.setRequestFactory(requestFactory); // 设置自定义错误处理器 restTemplate.setErrorHandler(new NoOpErrorHandler());
这样配置后,即使遇到401状态码,你也可以正常调用response.getBody()读取响应体了。
方案2:保留错误处理,同时捕获响应体
如果你不想完全跳过错误处理,希望在抛出异常时依然能拿到响应体,可以继承默认的DefaultResponseErrorHandler,在处理错误时先读取响应体再抛异常。
自定义带响应体的错误处理器
import org.springframework.http.client.ClientHttpResponse; import org.springframework.web.client.DefaultResponseErrorHandler; import org.springframework.web.client.HttpClientErrorException; import java.io.IOException; import java.nio.charset.StandardCharsets; import org.springframework.util.StreamUtils; public class CustomErrorHandler extends DefaultResponseErrorHandler { @Override public void handleError(ClientHttpResponse response) throws IOException { // 先读取响应体 String responseBody = StreamUtils.copyToString(response.getBody(), StandardCharsets.UTF_8); // 重新抛出包含响应体的异常 throw new HttpClientErrorException( response.getStatusCode(), response.getStatusText(), response.getHeaders(), responseBody.getBytes(StandardCharsets.UTF_8), null ); } }
使用时捕获异常获取响应体
try { ResponseEntity<String> response = restTemplate.getForEntity("your-request-url", String.class); } catch (HttpClientErrorException e) { // 从异常中获取响应体 String errorResponseBody = e.getResponseBodyAsString(); // 处理响应体逻辑 System.out.println("401响应体:" + errorResponseBody); }
方案3:用拦截器提前缓存响应体
还有一种思路是通过拦截器,在响应返回的第一时间就读取并缓存响应体,同时重置输入流,保证后续代码能正常读取。这种方式不管状态码是否异常,都能拿到响应体。
响应日志拦截器代码
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.BufferedInputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; import org.springframework.util.StreamUtils; public class ResponseLoggingInterceptor implements ClientHttpRequestInterceptor { @Override public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException { ClientHttpResponse response = execution.execute(request, body); // 读取响应体(因为用了BufferingClientHttpRequestFactory,流是可重复读取的) String responseBody = StreamUtils.copyToString(response.getBody(), StandardCharsets.UTF_8); // 这里可以做日志记录或者缓存逻辑 System.out.println("请求URL:" + request.getURI()); System.out.println("响应状态:" + response.getStatusCode()); System.out.println("响应体:" + responseBody); // 重置输入流,保证后续代码能再次读取 ((BufferedInputStream) response.getBody()).reset(); return response; } }
添加拦截器到RestTemplate
restTemplate.getInterceptors().add(new ResponseLoggingInterceptor());
总结
三种方案各有侧重:
- 方案1适合只需要读取响应体、不需要错误拦截的场景;
- 方案2适合需要保留错误处理逻辑,同时获取响应体的场景;
- 方案3适合全局记录所有请求响应的场景,不管状态是否正常。
内容的提问来源于stack exchange,提问作者Salih Erikci
相关产品推荐
相关产品推荐

