在Spring Boot项目中使用RestTemplate发送请求时如何处理响应?

在 Spring Boot 中使用 RestTemplate 处理响应的核心是选择合适的请求方法 (如 getForObject/getForEntity/exchange),并根据响应类型(字符串、实体类、复杂集合、自定义格式)进行解析,同时处理响应状态码、响应头和异常。以下是分场景的详细讲解,覆盖 90% 实际开发需求:

一、核心响应处理方法对比

RestTemplate 提供了 3 类核心方法处理响应,需根据需求选择:

方法类型 示例(GET) 特点 适用场景
xxxForObject getForObject(...) 直接返回响应体(忽略状态码/响应头),解析失败直接抛异常 只关心响应体、场景简单
xxxForEntity getForEntity(...) 返回 ResponseEntity<T>,包含响应体+状态码+响应头+响应Cookie 需要获取状态码/响应头
exchange exchange(...) 通用方法,支持所有 HTTP 方法,可自定义请求头,解析复杂类型更灵活 复杂场景(如解析 List/嵌套JSON)

注:xxxForObject/xxxForEntity 有 GET/POST/PUT/DELETE 对应方法(如 postForObject/deleteForEntity),exchange 是万能方法,适配所有场景。

二、基础响应处理场景

场景 1:解析响应为字符串(快速调试/未知响应结构)

适用于调试阶段,先获取原始响应字符串,再分析结构:

java 复制代码
@Service
public class ResponseService {
    @Autowired
    private RestTemplate restTemplate;

    // 解析响应为 String
    public String getResponseAsString() {
        String url = "http://localhost:8081/user/1";
        // getForObject 直接返回字符串
        String rawResponse = restTemplate.getForObject(url, String.class);
        System.out.println("原始响应:" + rawResponse);
        return rawResponse;
    }
}

输出示例(JSON 字符串):

复制代码
原始响应:{"id":1,"username":"张三","age":25}
场景 2:解析响应为自定义实体类(最常用)

将 JSON 响应自动映射到实体类(基于 Jackson 序列化,需保证字段名匹配):

java 复制代码
// 1. 定义响应实体类(与目标服务返回的 JSON 字段对应)
@Data // Lombok 注解,省略 getter/setter
public class UserDTO {
    private Long id;
    private String username;
    private Integer age;
    // 字段名不一致时,用 @JsonProperty 映射
    @JsonProperty("create_time") 
    private LocalDateTime createTime;
}

// 2. 解析为实体类
public UserDTO getResponseAsEntity() {
    String url = "http://localhost:8081/user/1";
    // 方式1:getForObject 直接返回实体类
    UserDTO user = restTemplate.getForObject(url, UserDTO.class);
    
    // 方式2:getForEntity 获取完整响应(含状态码)
    ResponseEntity<UserDTO> responseEntity = restTemplate.getForEntity(url, UserDTO.class);
    UserDTO userFromEntity = responseEntity.getBody(); // 提取响应体
    HttpStatus statusCode = responseEntity.getStatusCode(); // 获取状态码(如 200 OK)
    int statusCodeValue = responseEntity.getStatusCodeValue(); // 状态码数值(200)
    
    System.out.println("状态码:" + statusCode + ",用户信息:" + userFromEntity);
    return user;
}

输出示例:

复制代码
状态码:200 OK,用户信息:UserDTO(id=1, username=张三, age=25, createTime=2025-12-05T10:00:00)

三、进阶响应处理场景

场景 1:获取响应头/响应Cookie

通过 ResponseEntity 可获取响应头(如 Token、Content-Type)、Cookie 等元数据:

java 复制代码
public void getResponseHeaderAndCookie() {
    String url = "http://localhost:8081/user/1";
    ResponseEntity<UserDTO> response = restTemplate.getForEntity(url, UserDTO.class);

    // 1. 获取单个响应头(如 Token)
    String token = response.getHeaders().getFirst("X-Token");
    System.out.println("响应头 Token:" + token);

    // 2. 获取所有响应头
    HttpHeaders headers = response.getHeaders();
    headers.forEach((key, values) -> {
        System.out.println(key + ":" + String.join(",", values));
    });

    // 3. 获取响应Cookie(如需)
    List<String> cookieHeaders = headers.get(HttpHeaders.SET_COOKIE);
    if (cookieHeaders != null) {
        System.out.println("响应Cookie:" + cookieHeaders);
    }
}

输出示例:

复制代码
响应头 Token:abc123456
Content-Type:application/json
Transfer-Encoding:chunked
响应Cookie:JSESSIONID=789abc; Path=/; HttpOnly
场景 2:解析响应为 List/复杂集合

getForObject 无法直接解析 List<T>(会报类型转换异常),需用 exchange + ParameterizedTypeReference

java 复制代码
// 解析响应为 List<UserDTO>
public List<UserDTO> getResponseAsList() {
    String url = "http://localhost:8081/user?pageNum=1&pageSize=10";
    
    // 定义泛型类型(ParameterizedTypeReference 解决泛型擦除问题)
    ParameterizedTypeReference<List<UserDTO>> typeRef = new ParameterizedTypeReference<List<UserDTO>>() {};
    
    // exchange 通用方法:URL + 请求方法 + 请求体(GET 为 null) + 泛型类型
    ResponseEntity<List<UserDTO>> response = restTemplate.exchange(
            url,
            HttpMethod.GET,
            null, // GET 请求无请求体,传 null
            typeRef
    );
    
    List<UserDTO> userList = response.getBody();
    System.out.println("用户列表:" + userList);
    return userList;
}
场景 3:解析嵌套 JSON 响应(如分页结果)

若响应是嵌套结构(如分页数据),需定义匹配的嵌套实体类:

json 复制代码
// 目标服务返回的分页响应
{
  "code": 200,
  "msg": "success",
  "data": {
    "list": [{"id":1,"username":"张三"}, {"id":2,"username":"李四"}],
    "total": 100,
    "pageNum": 1,
    "pageSize": 10
  }
}

定义嵌套实体类:

java 复制代码
// 外层响应类
@Data
public class PageResponse<T> {
    private Integer code;
    private String msg;
    private PageData<T> data; // 嵌套的分页数据
}

// 分页数据类
@Data
public class PageData<T> {
    private List<T> list;
    private Long total;
    private Integer pageNum;
    private Integer pageSize;
}

解析嵌套响应:

java 复制代码
public PageResponse<UserDTO> getNestedResponse() {
    String url = "http://localhost:8081/user/page?pageNum=1&pageSize=10";
    
    // 定义嵌套泛型类型
    ParameterizedTypeReference<PageResponse<UserDTO>> typeRef = new ParameterizedTypeReference<PageResponse<UserDTO>>() {};
    
    ResponseEntity<PageResponse<UserDTO>> response = restTemplate.exchange(
            url,
            HttpMethod.GET,
            null,
            typeRef
    );
    
    PageResponse<UserDTO> pageResponse = response.getBody();
    System.out.println("总条数:" + pageResponse.getData().getTotal());
    System.out.println("用户列表:" + pageResponse.getData().getList());
    return pageResponse;
}
场景 4:处理非 JSON 响应(如 XML、表单)

RestTemplate 默认内置 JSON 转换器(MappingJackson2HttpMessageConverter),如需处理 XML/表单,需添加对应消息转换器:

步骤 1:引入 XML 依赖(如需解析 XML)
xml 复制代码
<dependency>
    <groupId>com.fasterxml.jackson.dataformat</groupId>
    <artifactId>jackson-dataformat-xml</artifactId>
</dependency>
步骤 2:配置消息转换器
java 复制代码
@Bean
public RestTemplate restTemplate() {
    RestTemplate restTemplate = new RestTemplate();
    // 获取默认消息转换器列表
    List<HttpMessageConverter<?>> converters = restTemplate.getMessageConverters();
    
    // 1. 添加 XML 转换器(支持解析 XML 响应)
    converters.add(new MappingJackson2XmlHttpMessageConverter());
    
    // 2. (可选)添加表单转换器(解析 application/x-www-form-urlencoded 响应)
    converters.add(new FormHttpMessageConverter());
    
    restTemplate.setMessageConverters(converters);
    return restTemplate;
}
步骤 3:解析 XML 响应
java 复制代码
// 假设目标服务返回 XML 响应:<UserDTO><id>1</id><username>张三</username></UserDTO>
public UserDTO getXmlResponse() {
    String url = "http://localhost:8081/user/xml/1";
    ResponseEntity<UserDTO> response = restTemplate.getForEntity(url, UserDTO.class);
    return response.getBody();
}

四、响应异常处理

RestTemplate 会根据响应状态码抛出异常,需捕获并处理(避免程序崩溃):

1. 异常类型说明
异常类型 触发场景
HttpClientErrorException 4xx 状态码(如 400 入参错误、404 资源不存在、401 未授权)
HttpServerErrorException 5xx 状态码(如 500 服务端错误、503 服务不可用)
ResourceAccessException 连接超时、拒绝连接、DNS 解析失败等(网络问题)
RestClientException 所有 RestTemplate 异常的父类(兜底捕获)
2. 异常捕获示例
java 复制代码
public UserDTO handleResponseException(Long userId) {
    String url = "http://localhost:8081/user/{id}";
    try {
        ResponseEntity<UserDTO> response = restTemplate.getForEntity(url, UserDTO.class, userId);
        
        // 主动校验状态码(可选,即使 2xx 也可自定义逻辑)
        if (response.getStatusCode() != HttpStatus.OK) {
            System.out.println("响应状态码异常:" + response.getStatusCode());
            return null;
        }
        return response.getBody();
        
    } catch (HttpClientErrorException.NotFound e) {
        // 捕获 404 异常
        System.out.println("用户不存在:" + e.getResponseBodyAsString());
        return null;
    } catch (HttpClientErrorException.Unauthorized e) {
        // 捕获 401 异常
        System.out.println("未授权访问:" + e.getResponseBodyAsString());
        return null;
    } catch (HttpServerErrorException e) {
        // 捕获 5xx 异常
        System.out.println("服务端错误:" + e.getStatusCode() + ",原因:" + e.getResponseBodyAsString());
        return null;
    } catch (ResourceAccessException e) {
        // 捕获网络超时/连接失败
        System.out.println("服务连接失败:" + e.getMessage());
        return null;
    } catch (RestClientException e) {
        // 兜底捕获所有 RestTemplate 异常
        System.out.println("请求失败:" + e.getMessage());
        return null;
    }
}

五、自定义响应解析配置

1. 设置响应编码(解决中文乱码)

若响应中文乱码,需自定义消息转换器的编码:

java 复制代码
@Bean
public RestTemplate restTemplate() {
    RestTemplate restTemplate = new RestTemplate();
    
    // 遍历默认转换器,修改 JSON 转换器的编码为 UTF-8
    for (HttpMessageConverter<?> converter : restTemplate.getMessageConverters()) {
        if (converter instanceof MappingJackson2HttpMessageConverter) {
            ((MappingJackson2HttpMessageConverter) converter).setDefaultCharset(StandardCharsets.UTF_8);
        }
    }
    return restTemplate;
}
2. 忽略响应状态码异常(仅特殊场景)

若不想让 RestTemplate 抛出 4xx/5xx 异常,可自定义 ResponseErrorHandler

java 复制代码
@Bean
public RestTemplate restTemplate() {
    RestTemplate restTemplate = new RestTemplate();
    
    // 自定义错误处理器:不抛出异常
    restTemplate.setErrorHandler(new ResponseErrorHandler() {
        @Override
        public boolean hasError(ClientHttpResponse response) throws IOException {
            // 所有状态码都视为"无错误",不抛出异常
            return false;
        }

        @Override
        public void handleError(ClientHttpResponse response) throws IOException {
            // 空实现,不处理错误
        }
    });
    return restTemplate;
}

注意:此配置需手动校验状态码,否则会忽略真实错误,仅适用于需兼容非标准状态码的场景。

总结

RestTemplate 处理响应的核心思路:

  1. 简单场景 :用 xxxForObject 直接解析响应体(String/实体类);
  2. 需状态码/响应头 :用 xxxForEntity 获取 ResponseEntity
  3. 复杂结构(List/嵌套) :用 exchange + ParameterizedTypeReference
  4. 异常处理 :捕获 HttpClientErrorException/ResourceAccessException 等,分场景处理;
  5. 自定义解析:通过消息转换器适配 XML/表单,或调整编码解决乱码。

根据实际需求选择对应方法,优先保证异常处理和类型解析的准确性,避免因泛型擦除、状态码未校验导致的问题。

相关推荐
IT_陈寒1 小时前
Redis 性能骤降50%?这5个隐藏配置陷阱你可能从未注意过
前端·人工智能·后端
jiuweiC1 小时前
python 虚拟环境-windows
开发语言·windows·python
谷哥的小弟1 小时前
Spring Framework源码解析——AnnotationAwareOrderComparator
java·后端·spring·源码
谷哥的小弟2 小时前
Spring Framework源码解析——StringUtils
java·后端·spring·源码
小张快跑。2 小时前
【Java企业级开发】(十)SpringBoot框架+项目实践
java·数据库·spring boot
PieroPC2 小时前
用 nicegui 3.0 + sqlite3 做个简单博客
前端·后端
天草二十六_简村人2 小时前
jenkins打包制作Python镜像,并推送至docker仓库,部署到k8s
后端·python·docker·容器·kubernetes·jenkins
JIngJaneIL2 小时前
基于Java二手交易管理系统(源码+数据库+文档)
java·开发语言·数据库·vue.js·spring boot
辜月十2 小时前
Docker-Compose 【Mysql】
后端