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

Spring RestTemplate exchange非200响应抛出HttpClientException问题

这个问题其实是RestTemplate的默认行为导致的——它的默认错误处理器会在收到**4xx(客户端错误)或5xx(服务端错误)**状态码时直接抛出异常,而不是把完整响应返回给你处理。你看到异常里显示响应体为null,其实是异常的默认输出误导了你,异常对象本身是包含响应体、状态码和响应头的。

下面给你两种实用的解决思路,按需选择:

方法1:捕获异常并提取响应信息

如果只是个别请求需要处理非200响应,最简单的方式就是在调用exchange时包裹try-catch块,直接从异常对象里提取你需要的响应内容。

修改后的客户端代码示例:

public String callFruitBasket(Fruit fruitRequest) {
    HttpHeaders requestHeaders = new HttpHeaders();
    requestHeaders.setContentType(MediaType.APPLICATION_JSON);
    HttpEntity<Fruit> fruit = new HttpEntity<>(fruitRequest, requestHeaders);
    System.out.println("Fruit headers: " + fruit.getHeaders());

    try {
        ResponseEntity<String> response = restTemplate.exchange(fruitBasketUrl, HttpMethod.POST, fruit, String.class);
        System.out.println("Full response: " + response);
        return response.getBody();
    } catch (HttpClientErrorException e) {
        // 处理4xx类型的错误响应
        System.out.println("Error status code: " + e.getStatusCode());
        System.out.println("Error response headers: " + e.getResponseHeaders());
        String errorBody = e.getResponseBodyAsString();
        System.out.println("Error response body: " + errorBody);
        // 根据业务需求返回错误体或自定义内容
        return errorBody;
    } catch (HttpServerErrorException e) {
        // 处理5xx类型的服务端错误
        System.out.println("Server error status code: " + e.getStatusCode());
        return e.getResponseBodyAsString();
    }
}

方法2:自定义错误处理器(全局生效)

如果你的项目里大部分请求都需要处理非200响应,不想每次都写重复的try-catch,可以自定义一个ResponseErrorHandler,让RestTemplate遇到非200状态时不抛出异常,直接返回完整响应。

第一步:创建自定义错误处理器

import org.springframework.http.client.ClientHttpResponse;
import org.springframework.web.client.DefaultResponseErrorHandler;
import java.io.IOException;

public class CustomResponseErrorHandler extends DefaultResponseErrorHandler {
    @Override
    public boolean hasError(ClientHttpResponse response) throws IOException {
        // 返回false表示所有状态码都不视为"错误",不会触发异常抛出
        // 如果你只想忽略特定状态码,可以在这里做判断,比如:
        // return super.hasError(response) && !response.getStatusCode().is4xxClientError();
        return false;
    }

    @Override
    public void handleError(ClientHttpResponse response) throws IOException {
        // 因为hasError返回false,这个方法不会被调用,留空即可
    }
}

第二步:给RestTemplate绑定这个处理器

在你初始化RestTemplate的配置类或代码块里添加:

RestTemplate restTemplate = new RestTemplate();
restTemplate.setErrorHandler(new CustomResponseErrorHandler());

之后再调用exchange,不管是200、400还是404,都会返回完整的ResponseEntity,你可以按需判断状态码处理:

public String callFruitBasket(Fruit fruitRequest) {
    HttpHeaders requestHeaders = new HttpHeaders();
    requestHeaders.setContentType(MediaType.APPLICATION_JSON);
    HttpEntity<Fruit> fruit = new HttpEntity<>(fruitRequest, requestHeaders);
    System.out.println("Fruit headers: " + fruit.getHeaders());

    ResponseEntity<String> response = restTemplate.exchange(fruitBasketUrl, HttpMethod.POST, fruit, String.class);
    System.out.println("Full response: " + response);
    
    // 根据状态码分支处理
    if (response.getStatusCode().is2xxSuccessful()) {
        return response.getBody();
    } else {
        System.out.println("Non-success status: " + response.getStatusCode());
        return response.getBody();
    }
}

补充说明

  • 你之前看到的异常信息里的response body null只是异常默认toString()的输出形式,实际HttpClientErrorException对象里是存储了完整响应体的,调用getResponseBodyAsString()就能获取到。
  • 两种方法各有适用场景:方法1适合局部请求的特殊处理,方法2适合全局统一处理非200响应的需求。

内容的提问来源于stack exchange,提问作者iranicus

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 07:03:22