You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

当响应码为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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 07:37:24