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"; // 重试全部失败后的降级处理
}
}