Spring提供了一种简单便捷的模板类 RestTemplate 来调用 RESTful 接口。它提供了多种便捷访问HTTP服务的方法,能够大大提高客户端的编写效率。
RestTemplate方法 | HTTP方法 |
|---|---|
getForEntity | GET |
getForObject | GET |
postForEntity | POST |
postForObject | POST |
put | PUT |
patch | PATCH |
delete | DELETE |
exchange | any |
execute | any |
@RestController
@Slf4j
public class RestfulController {
@GetMapping(value = "/getUser1", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public User getUser1() {
return new User(1L, "zhaoxb");
}
@GetMapping(value = "/getUser2", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public User getUser2(User user) {
log.info("getUser2:{}", JSONUtil.toJsonPrettyStr(user));
return user;
}
@PostMapping(value = "/postUser", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public User postUser(User user) {
log.info("postUser:{}", JSONUtil.toJsonPrettyStr(user));
return user;
}
@PostMapping(value = "/postBody", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public User postBody(@RequestBody User user) {
log.info("postBody:{}", JSONUtil.toJsonPrettyStr(user));
return user;
}
}GET请求,不带参。GET请求,带参。POST请求,带参。POST请求,带有请求体。实体类,需要提供有参和无参构造
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private Long id;
private String name;
}RestTemplate配置类
@Configuration
public class RestConfig {
@Bean
public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) {
RestTemplate restTemplate = restTemplateBuilder
.setConnectTimeout(Duration.ofMillis(5000L))
.setReadTimeout(Duration.ofMillis(30000L))
.build();
return restTemplate;
}
}@ActiveProfiles("test")
@RunWith(SpringRunner.class)
@SpringBootTest
@Slf4j
public class RestfulGetTests {
@Autowired
private RestTemplate restTemplate;
@Test
public void getForEntity1() {
ResponseEntity<String> responseEntity = restTemplate.getForEntity("http://127.0.0.1:31000/getUser1", String.class);
log.info("响应码:{}", responseEntity.getStatusCodeValue());
log.info("响应体:{}", JSONUtil.toJsonPrettyStr(responseEntity.getBody()));
}
}响应码:200
响应体:{
"name": "zhaoxb",
"id": 1
}@Test
public void getForEntity2() {
ResponseEntity<String> responseEntity = restTemplate.getForEntity("http://127.0.0.1:31000/getUser2?id={1}&name={2}", String.class, 2, "zhaoxb");
log.info("响应体:{}", JSONUtil.toJsonPrettyStr(responseEntity.getBody()));
}用一个数字做占位符。最后是一个可变长度的参数,用来替换前面的占位符。
响应体:{
"name": "zhaoxb",
"id": 2
}@Test
public void getForEntity3() {
Map<String, Object> map = new HashMap<>();
map.put("id", 3);
map.put("name", "zhaoxb");
ResponseEntity<String> responseEntity = restTemplate.getForEntity("http://127.0.0.1:31000/getUser2?id={id}&name={name}", String.class, map);
log.info("响应体:{}", JSONUtil.toJsonPrettyStr(responseEntity.getBody()));
}使用 name={name} 这种形式。最后一个参数是map,map的key为前边占位符的名字,value为实际参数值。
响应体:{
"name": "zhaoxb",
"id": 3
}@Test
public void getForEntity4() {
ResponseEntity<User> responseEntity = restTemplate.getForEntity("http://127.0.0.1:31000/getUser1", User.class);
log.info("响应体:{}", JSONUtil.toJsonPrettyStr(responseEntity.getBody()));
}响应体:{
"name": "zhaoxb",
"id": 1
}@Test
public void getForObject() {
User User = restTemplate.getForObject("http://127.0.0.1:31000/getUser1", User.class);
log.info("响应体:{}", JSONUtil.toJsonPrettyStr(User));
}getForObject是对getForEntity函数的进一步封装,只关注返回消息的实体内容。
响应体:{
"name": "zhaoxb",
"id": 1
}用post方法发送带参的请求时,Map不能被定义为 HashMap、LinkedHashMap,而应被定义为 LinkedMultiValueMap,这样参数才能成功传递到后台。
@Test
public void postForEntity() {
MultiValueMap<String, Object> multiValueMap = new LinkedMultiValueMap<>();
multiValueMap.add("id", 5);
multiValueMap.add("name", "zhaoxb");
ResponseEntity<User> responseEntity = restTemplate.postForEntity("http://127.0.0.1:31000/postUser", multiValueMap, User.class);
log.info("响应码:{}", responseEntity.getStatusCodeValue());
log.info("响应体:{}", JSONUtil.toJsonPrettyStr(responseEntity.getBody()));
}响应体:{
"name": "zhaoxb",
"id": 5
}@Test
public void postForObject() {
MultiValueMap<String, Object> multiValueMap = new LinkedMultiValueMap<>();
multiValueMap.add("id", 6);
multiValueMap.add("name", "zhaoxb");
User user = restTemplate.postForObject("http://127.0.0.1:31000/postUser", multiValueMap, User.class);
log.info("响应体:{}", JSONUtil.toJsonPrettyStr(user));
}和 getForObject 相对应,只关注返回的消息体。
响应体:{
"name": "zhaoxb",
"id": 6
}@Test
public void postForObject2() {
User reqUser = new User(10L, "zhaoxb");
User user = restTemplate.postForObject("http://127.0.0.1:31000/postBody", reqUser, User.class);
log.info("响应体:{}", JSONUtil.toJsonPrettyStr(user));
}RestTemplate底层实现序列化和反序列化。
响应体:{
"name": "zhaoxb",
"id": 10
}@Test
public void exchange() {
MultiValueMap<String, Object> multiValueMap = new LinkedMultiValueMap<>();
multiValueMap.add("id", 7);
multiValueMap.add("name", "zhaoxb");
HttpEntity<MultiValueMap<String, Object>> httpEntity = new HttpEntity<>(multiValueMap);
// HttpEntity<MultiValueMap<String, Object>> httpEntityWithHeaders = new HttpEntity<>(multiValueMap, new HttpHeaders());
ResponseEntity<User> responseEntity = restTemplate.exchange("http://127.0.0.1:31000/postUser", HttpMethod.POST, httpEntity, User.class);
log.info("响应体:{}", JSONUtil.toJsonPrettyStr(responseEntity.getBody()));
}HttpEntity还支持带有HTTP请求头的构造方法。
响应体:{
"name": "zhaoxb",
"id": 7
}用RestTemplate发送PUT、PATCH、DELETE方法与GET、POST方法非常类似,这里不做展开。
ClientHttpRequestFactory是Spring定义的一个接口,用于生产ClientHttpRequest对象,RestTemplate只是模板类,抽象了很多调用方法,而底层真正使用何种框架发送HTTP请求是通过ClientHttpRequestFactory指定的。
RestTemplate默认使用的是SimpleClientHttpRequestFactory,其内部使用的是JDK的java.net.HttpURLConnection创建底层连接,默认是没有连接池的。可以通过 setRequestFactory 函数设置不同的HTTP源,比如 Apache HttpComponents、Netty和OkHttp。
设置Apache HttpComponents为HTTP客户端源
<dependencies>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
</dependencies>@Configuration
public class RestConfig {
@Bean
public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) {
RestTemplate restTemplate = restTemplateBuilder
.setConnectTimeout(Duration.ofMillis(5000L))
.setReadTimeout(Duration.ofMillis(30000L))
.requestFactory(() -> clientHttpRequestFactory())
.build();
return restTemplate;
}
@Bean
public ClientHttpRequestFactory clientHttpRequestFactory() {
HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
//开始设置连接池
PoolingHttpClientConnectionManager poolingHttpClientConnectionManager = new PoolingHttpClientConnectionManager();
poolingHttpClientConnectionManager.setMaxTotal(100); //最大连接数
poolingHttpClientConnectionManager.setDefaultMaxPerRoute(20); //同路由并发数
httpClientBuilder.setConnectionManager(poolingHttpClientConnectionManager);
HttpClient httpClient = httpClientBuilder.build();
// httpClient连接配置
HttpComponentsClientHttpRequestFactory clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory(httpClient);
// 设置超时,如果 RestTemplateBuilder 已经设置,这里就不需要设置
// clientHttpRequestFactory.setConnectTimeout(5 * 1000); // 连接超时
// clientHttpRequestFactory.setReadTimeout(30 * 1000); // 数据读取超时时间
clientHttpRequestFactory.setConnectionRequestTimeout(30 * 1000); //连接不够用的等待时间
return clientHttpRequestFactory;
}
}RestTemplate默认使用 jackson 来实现序列化和反序列化,默认情况下会注册MIME类型的转换器,但可以通过 setMessageConverters 函数指定其他类型的转化器。
这里其实也可以用FastJson库的FastJsonHttpMessageConverter4类来做转换器,只是近些年FastJson屡爆漏洞,还是建议用默认的jackson来实现。