一、简介
RestTemplate 是 Spring 提供的用于访问 Rest 服务的客户端,RestTemplate 提供了多种便捷访问远程 HTTP 服务的方法,能够大大提高客户端的编写效率。相较于之前常用的 HttpClient,RestTemplate 是一种更优雅的调用 RESTful 服务的方式。
二、RestTemplate 用法
2.1 准备工作
创建一个用户实体类
public class User {
private String username;
public User(){}
public User(String username){
this.username = username;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
}
创建 Controller:
@RestController
public class UserController {
private static final Logger log = LoggerFactory.getLogger(UserController.class);
@GetMapping("/user/{username}")
public User getUser(@PathVariable String username) {
log.info("username :{}", username);
return new User(username);
}
@PostMapping("/user")
public User modify(String username){
log.info("username :{}", username);
return new User("modify");
}
}
2.2 声明 RestTemplate
* Spring Boot <= 1.3 不需要定义,Spring Boot自动定义了一个
* Spring Boot >= 1.4 Spring Boot不再自动定义一个RestTemplate,而是定义了一个RestTemplateBuilder 可以更好地控制 RestTemplate 创建的对象
@Configuration
public class ApplicationConfig {
@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder.build();
}
}
2.3 GET 请求
方法列表:
<T> T getForObject(String url, Class<T> responseType, Object... uriVariables);
<T> T getForObject(String url, Class<T> responseType, Map<String, ?> uriVariables);
<T> T getForObject(URI url, Class<T> responseType);
<T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Object... uriVariables);
<T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Map<String, ?> uriVariables);
<T> ResponseEntity<T> getForEntity(URI var1, Class<T> responseType);
实例一:
getForObject 方法返回对象为响应体中数据转化成的对象
@SpringBootTest
class UserApiTests {
@Autowired
private RestTemplate restTemplate;
@Test
public void shouldGetRequestSuccess() {
User expected = new User("MarkLogZhu");
String url = "http://localhost:8900/user/{1}";
User result = restTemplate.getForObject(url, User.class,"MarkLogZhu");
assertTrue(expected.getUsername().equals(result.getUsername()));
}
}
实例二:
getForEntity 方法返回对象为 ResponseEntity 对象,包含了响应中的一些重要信息,比如响应头、响应状态码、响应体等
@SpringBootTest
class UserApiTests {
@Autowired
private RestTemplate restTemplate;
@Test
public void shouldGetForEntityRequestSuccess() {
User expected = new User("MarkLogZhu");
String url = "http://localhost:8900/user/{1}";
ResponseEntity<User> result = restTemplate.getForEntity(url,User.class,"MarkLogZhu");
assertTrue(result.getStatusCode().is2xxSuccessful());
assertTrue(expected.getUsername().equals(result.getBody().getUsername()));
}
}
2.4 POST 请求
方法列表:
<T> T postForObject(String url, @Nullable Object request, Class<T> responseType, Object... uriVariables);
<T> T postForObject(String url, @Nullable Object request, Class<T> responseType, Map<String, ?> uriVariables);
<T> T postForObject(URI url, @Nullable Object request, Class<T> responseType);
<T> ResponseEntity<T> postForEntity(String url, @Nullable Object request, Class<T> responseType, Object... uriVariables);
<T> ResponseEntity<T> postForEntity(String url, @Nullable Object request, Class<T> responseType, Map<String, ?> uriVariables);
<T> ResponseEntity<T> postForEntity(URI url, @Nullable Object request, Class<T> responseType);
实例一:
getForObject 方法返回对象为响应体中数据转化成的对象
@SpringBootTest
class UserApiTests {
@Autowired
private RestTemplate restTemplate;
@Test
public void shouldPostForObjectRequestSuccess() {
User expected = new User("modify");
String url = "http://localhost:8900/user";
Map<String, Object> map = new HashMap<>();
map.put("username", "MarkLogZhu");
User result = restTemplate.postForObject(url, map, User.class);
assertTrue(expected.getUsername().equals(result.getUsername()));
}
}
实例二:
getForEntity 方法返回对象为 ResponseEntity 对象,包含了响应中的一些重要信息,比如响应头、响应状态码、响应体等
@SpringBootTest
class UserApiTests {
@Autowired
private RestTemplate restTemplate;
@Test
public void shouldPostForEntityRequestSuccess() {
User expected = new User("modify");
String url = "http://localhost:8900/user";
Map<String, Object> map = new HashMap<>();
map.put("username", "MarkLogZhu");
ResponseEntity<User> result = restTemplate.postForEntity(url, map, User.class);
assertTrue(result.getStatusCode().is2xxSuccessful());
assertTrue(expected.getUsername().equals(result.getBody().getUsername()));
}
}
2.5 PUT请求方法
方法列表:
void put(String url, @Nullable Object request, Object... uriVariables);
void put(String url, @Nullable Object request, Map<String, ?> uriVariables);
void put(URI url, @Nullable Object request);
2.6 DELETE请求方法
方法列表:
void delete(String url, Object... uriVariables);
void delete(String url, Map<String, ?> uriVariables);
void delete(URI url);
三、RestTemplate 的组成
RestTemplate 包含如下几个部分:
* ClientHttpRequestFactory 客户端请求对象工厂
* ClientHttpRequestInterceptor 请求拦截器
* HttpMessageConverter 对象转换器
* ResponseErrorHandler 异常处理
3.1 客户端请求对象工厂
ClientHttpRequestFactory 默认使用的是 JDK 的 HttpURLConnection :
@FunctionalInterface
public interface ClientHttpRequestFactory {
ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException;
}
public class SimpleClientHttpRequestFactory implements ClientHttpRequestFactory, AsyncClientHttpRequestFactory {
......
@Override
public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException {
HttpURLConnection connection = openConnection(uri.toURL(), this.proxy);
prepareConnection(connection, httpMethod.name());
if (this.bufferRequestBody) {
return new SimpleBufferingClientHttpRequest(connection, this.outputStreaming);
}
else {
return new SimpleStreamingClientHttpRequest(connection, this.chunkSize, this.outputStreaming);
}
}
......
}
可以通过 setRequestFactory(ClientHttpRequestFactory requestFactory)
方法来更改请求实现。
3.2 请求拦截器
可以实现 ClientHttpRequestInterceptor 接口,自定义拦截器记录请求和响应头和主体:
public class RequestResponseLoggingInterceptor implements ClientHttpRequestInterceptor {
private final Logger log = LoggerFactory.getLogger(this.getClass());
@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
logRequest(request, body);
ClientHttpResponse response = execution.execute(request, body);
logResponse(response);
return response;
}
private void logRequest(HttpRequest request, byte[] body) throws UnsupportedEncodingException {
if (log.isDebugEnabled()){
log.debug("===========================request begin================================================");
log.debug("URI : {}", request.getURI());
log.debug("Method : {}", request.getMethod());
log.debug("Headers : {}", request.getHeaders());
log.debug("Request body: {}", new String(body, "UTF-8"));
log.debug("==========================request end================================================");
}
}
private void logResponse(ClientHttpResponse response) throws IOException {
if (log.isDebugEnabled()){
log.debug("============================response begin==========================================");
log.debug("Status code : {}", response.getStatusCode());
log.debug("Status text : {}", response.getStatusText());
log.debug("Headers : {}", response.getHeaders());
log.debug("Response body: {}", StreamUtils.copyToString(response.getBody(), Charset.defaultCharset()));
log.debug("=======================response end=================================================");
}
}
}
注册拦截器
@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
RestTemplate restTemplate = builder.build();
restTemplate.setInterceptors(Collections.singletonList(new RequestResponseLoggingInterceptor()));
// 默认的 ClientHttpRequestFactory 的响应只能被读取一次,会导致响应返回数据为空,可以使用 BufferingClientHttpRequestFactory ,它支持多次读取
restTemplate.setRequestFactory(new BufferingClientHttpRequestFactory(new SimpleClientHttpRequestFactory()));
return restTemplate;
}
3.3 对象转换器
从 RestTemplate 的构造函数来看,它默认支持 ByteArrayHttpMessageConverter 、StringHttpMessageConverter 和 ResourceHttpMessageConverter 这三个转换器:
public RestTemplate() {
this.messageConverters.add(new ByteArrayHttpMessageConverter());
this.messageConverters.add(new StringHttpMessageConverter());
this.messageConverters.add(new ResourceHttpMessageConverter(false));
try {
this.messageConverters.add(new SourceHttpMessageConverter<>());
}
catch (Error err) {
// Ignore when no TransformerFactory implementation is available
}
this.messageConverters.add(new AllEncompassingFormHttpMessageConverter());
if (romePresent) {
this.messageConverters.add(new AtomFeedHttpMessageConverter());
this.messageConverters.add(new RssChannelHttpMessageConverter());
}
if (jackson2XmlPresent) {
this.messageConverters.add(new MappingJackson2XmlHttpMessageConverter());
}
else if (jaxb2Present) {
this.messageConverters.add(new Jaxb2RootElementHttpMessageConverter());
}
if (jackson2Present) {
this.messageConverters.add(new MappingJackson2HttpMessageConverter());
}
else if (gsonPresent) {
this.messageConverters.add(new GsonHttpMessageConverter());
}
else if (jsonbPresent) {
this.messageConverters.add(new JsonbHttpMessageConverter());
}
if (jackson2SmilePresent) {
this.messageConverters.add(new MappingJackson2SmileHttpMessageConverter());
}
if (jackson2CborPresent) {
this.messageConverters.add(new MappingJackson2CborHttpMessageConverter());
}
this.uriTemplateHandler = initUriTemplateHandler();
}
也可以添加自定义的转换器,如微信接口返回是JSON格式的数据,但是响应头是Content-Type = text/plain,我们需要自定义一个转换器:
public class WeChatMessageConverter extends MappingJackson2HttpMessageConverter {
public WeChatMessageConverter() {
List<MediaType> mediaTypes = new ArrayList<>();
mediaTypes.add(MediaType.TEXT_PLAIN);
setSupportedMediaTypes(mediaTypes);
}
}
注册转换器:
restTemplate.getMessageConverters().add(new WeChatMessageConverter());