RestTemplate 使用

RestTemplate 是什么

RestTemplate 是 spring 提供的发送HTTP请求的同步客户端工具。

注意:RestTemplate 在 springboot3中已废弃,可使用 RestClient或者WebClient替代。

构造RestTemplate对象

通常的做法是将 RestTemplate 对象作为bean使用,一般有以下两种方式。

直接new

java 复制代码
@Configuration
public class RestTemplateConfig {
    
    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

}

RestTemplateBuilder 注入

java 复制代码
@Configuration
public class RestTemplateConfig {

    @Bean
    public RestTemplate restTemplate(RestTemplateBuilder builder) {
        return builder.build();
    }

}

实际生产环境中,更推荐使用这种方式,这种方式有个好处,就是可以通过spring配置文件进行配置。

比如配置:spring.resttemplate.read-timeout=5s 则会应用到 RestTemplateBuilder中。

常用方法

exchange

exchange 是比较通用的方法,可以发送GET,PUT,POST,DELETE 请求。

GET请求

方式一:没有参数

java 复制代码
ResponseEntity<String> responseEntity = restTemplate.exchange(
        "http://localhost:9090/api/hello", 
        HttpMethod.GET, 
        null,    // 无请求体
        String.class
);

方式二:带路径参数和请求参数:

java 复制代码
ResponseEntity<String> responseEntity = restTemplate.exchange(
        "http://localhost:9090/api/hello/{id}?name={name}",
        HttpMethod.GET,
        null,
        String.class,
        1,     // 参数在后面一一填入
        "CongVee"
);

方式三:url编码:

java 复制代码
URI url = UriComponentsBuilder.fromUri(URI.create("http://localhost:9090"))
        .path("/api/hello/{id}")
        .queryParam("name", "CongVee")
        .buildAndExpand(123)  // 设置路径参数
        .toUri();

ResponseEntity<String> responseEntity = restTemplate.exchange(
        url,
        HttpMethod.GET,
        null,
        String.class
);

POST请求

主要是看如何添加响应体。

java 复制代码
URI url = UriComponentsBuilder.fromUri(URI.create("http://localhost:9090"))
        .path("/api/hello/{id}")
        .buildAndExpand(123)  // 设置路径参数
        .toUri();

// 构造请求体
User user = new User();
user.setName("CongVee");
user.setAge(25);

ResponseEntity<String> responseEntity = restTemplate.exchange(
        url,
        HttpMethod.POST,
        new HttpEntity<>(user),
        String.class
);

PUT和DELETE请求也没必要再说,都是同样的方式添加路径参数、查询参数、请求体。

getForEntity

发送GET请求,获取响应。

java 复制代码
URI url = UriComponentsBuilder.fromUri(URI.create("http://localhost:9090"))
        .path("/api/hello/{id}")
        .queryParam("name", "CongVee")
        .buildAndExpand(123)  // 设置路径参数
        .toUri();

ResponseEntity<String> forEntity = restTemplate.getForEntity(url, String.class);

getForObject

发送GET请求,直接获取响应体。如果不关系响应的响应头和状态码,可以使用这种

java 复制代码
URI url = UriComponentsBuilder
        .fromPath("/api/hello/{id}")
        .queryParam("name", "CongVee")
        .buildAndExpand(123)  // 设置路径参数
        .toUri();


String body = restTemplate.getForObject(url, String.class);

postForEntity 和 postForObject

和get类似。

put

put请求一般也不关心响应体,所以也不区分Object还是Entity。

java 复制代码
URI url = UriComponentsBuilder
        .fromPath("/api/hello/{id}")
        .queryParam("name", "CongVee")
        .buildAndExpand(123)  // 设置路径参数
        .toUri();

User user = new User();
user.setName("CongVee");
user.setAge(25);

restTemplate.put(url, user);

delete

delete和put类似,也不关心响应体,并且delete一般也不传递请求体。

java 复制代码
URI url = UriComponentsBuilder
        .fromPath("/api/hello/{id}")
        .queryParam("name", "CongVee")
        .buildAndExpand(123)  // 设置路径参数
        .toUri();

restTemplate.delete(url);

配置 【待补充完整】

直接new

java 复制代码
@Configuration
public class RestTemplateConfig {

    @Bean
    public RestTemplate restTemplate(ClientHttpRequestFactory clientHttpRequestFactory) {
        return new RestTemplate(clientHttpRequestFactory);
    }

    @Bean
    public ClientHttpRequestFactory clientHttpRequestFactory() {
        HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();

        requestFactory.setConnectTimeout(5000); // 设置链接超时 5s
        requestFactory.setReadTimeout(10000);   // 设置读取超时 10s

        return requestFactory;
    }

}

需要指定一个 ClientHttpRequestFactory,ClientHttpRequestFactory 是一个接口,有很多实现类:

1、SimpleClientHttpRequestFactory

2、HttpComponentsClientHttpRequestFactory (生产环境推荐这个)

通过 RestTemplateBuilder

java 复制代码
@Configuration
public class RestTemplateConfig {

    @Bean
    public RestTemplate restTemplate(RestTemplateBuilder builder) {
        return builder
                .rootUri("http://localhost:8080")
                .connectTimeout(Duration.ofSeconds(5))
                .readTimeout(Duration.ofSeconds(10))
                .build();
    }

}

异常处理

RestTemplate 默认会将 HTTP 错误响应码(4xx和5xx)转换为特定的异常抛出。以下是关于这种机制及其处理方式的详细说明。

RestTemplate 抛出的异常主要是 RestClientException,而RestClientException又可以再细分:

1、HttpClientErrorException:4xx

2、HttpServerErrorException:5xx

3、ResourceAccessException:网络原因

4、xx

java 复制代码
String url = UriComponentsBuilder.fromUri(URI.create("http://localhost:9090"))
        .path("/api/hello/{id}")
        .queryParam("name", "CongVee")
        .buildAndExpand(123)  // 设置路径参数
        .toUriString();

try {
    ResponseEntity<String> responseEntity = restTemplate.exchange(
            url,
            HttpMethod.GET,
            null,
            String.class
    );

    // 成功响应,也就是 2xx
    log.info("Calling third party API");

    return responseEntity.getBody();

} catch (HttpClientErrorException e) {
    // 4xx
    log.error("Error calling third party API", e);
    throw e;
} catch (HttpServerErrorException e) {
    // 5xx 服务器错误
    log.error("Error calling third party API", e);
    throw e;
} catch (ResourceAccessException e) {
    // 网络错误, 通常需要重试
    log.error("Error calling third party API", e);
    throw e;
}

重试

一般对 5xx 和 网络问题进行重试,也就是 HttpServerErrorException 和 ResourceAccessException 异常进行重试。

java 复制代码
@Service
@RequiredArgsConstructor
public class ThirdPartyClient {

    private static final Logger log = LoggerFactory.getLogger(ThirdPartyClient.class);

    @Autowired
    private RestTemplate restTemplate;

    @Retryable(retryFor = {RestClientException.class}, 
               noRetryFor = {HttpClientErrorException.class}, 
               maxAttempts = 3, 
               backoff = @Backoff(delay = 5000))
    public String getHello() {

        URI url = UriComponentsBuilder
                .fromPath("/api/hello/{id}")
                .queryParam("name", "CongVee")
                .buildAndExpand(123)  // 设置路径参数
                .toUri();

        User user = new User();
        user.setName("CongVee");
        user.setAge(25);

        ResponseEntity<String> responseEntity = restTemplate.exchange(
                url,
                HttpMethod.POST,
                new HttpEntity<>(user),
                String.class
        );

        restTemplate.delete(url);

        // 只有 2xx 和 3xx 不会进入catch
        log.info("Calling third party API");


        return responseEntity.getBody();
    }

    @Recover
    public String recover(RestClientException e, String url) {
        return "Fallback Data"; // 重试全部失败后的降级处理
    }

}
相关推荐
neoooo9 分钟前
Spring Boot 中的 synchronized(this):到底锁住了谁?
java·spring boot·后端
白仑色1 小时前
Spring Boot 安全登录系统:前后端分离实现
spring boot·后端·安全
Java陈序员1 小时前
又一款基于 SpringBoot + Vue 实现的开源新零售商城系统!
vue.js·spring boot·uni-app
努力的小郑1 小时前
从 OOM 到秒级导入:EasyExcel 百万级数据优化实战(附可直接跑的工具类)
spring boot·excel
考虑考虑1 小时前
解决java: java.lang.ExceptionInInitializerError com.sun.tools.javac.code.TypeTag :
java·spring boot·后端
砍光二叉树2 小时前
【MYSQL8】springboot项目,开启ssl证书安全连接
spring boot·mysql·ssl
fanruitian9 小时前
Springboot aop面向切面编程
java·spring boot·spring
中国lanwp10 小时前
Spring Boot 中使用 Lombok 进行依赖注入的示例
java·spring boot·后端
张先shen14 小时前
Spring Boot集成Redis:从配置到实战的完整指南
spring boot·redis·后端
Q_Q51100828515 小时前
python的婚纱影楼管理系统
开发语言·spring boot·python·django·flask·node.js·php