RestTemplate的详细使用方法
在 Eureka 中,使用 RestTemplate 类进行服务调用,相信读者也能感觉到这个类还是比较实用。最重要的是,这个调用只要加上注解 @LoadBalanced,就可以使用 Ribbon 的客户端负载均衡。所以,本节将对这个类进行讲解。
在前面只讲解了简单的一种请求方式,也只有一种 API 的调用。但在实际场景中,我们四种 Restful 都是存在的,因此这里重点讲解 GET、POST、PUT、DELETE 的使用。
RestTemplate功能
首先,我们需要知道 RestTemplate 是由 Spring 提供的一个客户端,主要用于对 Rest 服务进行访问。在这个类中,提高了开发效率,使 HTTP 服务的通信得到简化,简化了提交表单的难度,还自带 Json 自动转换功能。
在默认情况下使用 JDK 的 HTTP 连接工具,但是可以通过属性切换不同的 HTTP 源,例如 Netty、OkHttp 或者 HttpComponents。RestTemplate 的方法如表11.1所示。

在这里主要介绍的是四种常用的请求方式,其他的先不进行介绍。为了对底层稍微有些了解,我们看看 GET 的其中一个方法,代码如下所示。
// GET
@Override
@Nullable
public <T> T getForObject(String url, Class<T> responseType, Object... uriVariables) throws RestClientException {
RequestCallback requestCallback = acceptHeaderRequestCallback (responseType);
HttpMessageConverterExtractor<T> responseExtractor =
new HttpMessageConverterExtractor<>(responseType, getMessageConverters(), logger);
return execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables);
}
在上面的代码中,在使用时只需要传递 responseType,然后内部方法默认会使用 HttpMessageConverter 实例将 HTTP 转成 pojo 或者将 pojo 转成 HTTP。
在这里顺便了解一下 HttpMessageConverter 的接口功能,代码如下所示。
package org.springframework.http.converter;
/**
* Strategy interface that specifies a converter that can convert from and to HTTP requests and responses.
*/
public interface HttpMessageConverter<T> {
//说明转换器是否可以读取给定的类
boolean canRead(Class<?> clazz, @Nullable MediaType mediaType);
//说明转换器是否可以写入给定的类
boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType);
//返回
List<MediaType> getSupportedMediaTypes();
//读取inputMessage
T read(Class<? extends T> clazz, HttpInputMessage inputMessage) throws IOException;
//往outputMessage中写Object
void write(T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage);
}
图11.5是 RestTemplate 的类关系图。

从图11.5中可以了解到,RestTemplate 实现了 RestOperations。这个 RestOperations 是一个定义了对 Rest 操作的基本集合,RestTemplate 实现这个接口,是对 Rest 操作的一次封装。在上图中有一个 HttpAccessor,这个类是基础的 HTTP 访问类,用来构建 HttpRequestFactory。
GET请求API
图11.6是主要 GET 的 API。

通过图11.6,说明 RestTemplate 访问 GET 主要有两种方式。
getForEntity方式
先说明这种方式让人更加容易理解。通过图11.6可以知道,这种方式有三个重载实现,并且在方法中将会返回 ResponseEntity 类型。先看什么是 ResponseEntity,代码如下所示。
public class ResponseEntity<T> extends HttpEntity<T>
{
public HttpStatus getStatusCode(){}
public int getStatusCodeValue(){}
public boolean equals(@Nullable Object other) {}
public String toString() {}
public static BodyBuilder status(HttpStatus status) {}
public static BodyBuilder ok() {}
public static <T> ResponseEntity<T> ok(T body) {}
public static BodyBuilder created(URI location) {} ...
}
从上面代码中可以知道,这里包含 HttpStatus 与 BodyBuilder 的信息,说明可以对 response 进行处理。这个对象 ResponseEntity 是 Spring 对于 HTTP 请求响应 response 的封装。
HttpStatus 是一个枚举类,包含了 HTTP 的请求状态;BodyBuilder 封装请求体对象;父类 HttpEntity 包含了请求头信息对象 HttpHeaders。
下面,我们看看提供出来的三个方法,主要是参数不同。
(1)public <T> ResponseEntity<T> getForEntity(String url, Class<T>responseType, Object… uriVariables)
在这个方法中,需要三个参数。第一个为 url,表示请求地址;第二个为 responseType,是请求响应体的封装类型;第三个为 uriVariables,表示不定参数。
使用方式主要是使用占位符,然后将参数放在第三个位置上。如果我们返回的是基本类型 String,代码如下所示。
RestTemplate restTemplate=new RestTemplate();
ResponseEntity<String>
responseEntity=restTemplate.getForEntity("http://HELLOSERVER/hello?id={1}",String.class,"100");
String bodyStr=responseEntity.getBody();
如果返回的响应体 Body 是一个对象,例如 User,则代码如下所示。
RestTemplate restTemplate=new RestTemplate();
ResponseEntity<User>
responseEntity=restTemplate.getForEntity("http://HELLOSERVER/hello?id={1}",User.class,"100");
User bodyStr=responseEntity.getBody();
在代码中,将会把占位符和第三个位置的数据进行替换。需要注意的是,如果存在多个参数,需要按照顺序进行书写。
(2)public <T> ResponseEntity<T> getForEntity(String url, Class<T>responseType, Map<String, ?> uriVariables)
将这个重载的方法与上面的方法进行对比,将会发现,第三个位置的参数使用 Map 类型进行封装。但是在使用时,不是简单地修改第三个位置的数据,还需要对 url 进行修改。在 url 中,占位符需要使用 Map 中的 key 作为参数。
使用方法如下,这里只使用 String 类型进行举例说明。
RestTemplate restTemplate=new RestTemplate();
Map<String> map=new HashMap<>();
map.put("id","100");
ResponseEntity<String>
responseEntity=restTemplate.getForEntity("http://HELLOSERVER/hello?id={id}",String.class,map);
String bodyStr=responseEntity.getBody();
(3)public <T> ResponseEntity<T> getForEntity(URI url, Class<T>responseType)
在这个方法中,使用 URI 对 url 与 uriVariavles 进行封装。
package java.net;
public final class URI implements Comparable<URI>, Serializable
URI 是 JDK 原始的封装类,标识一个资源标识符,使用方式如下。
RestTemplate restTemplate=new RestTemplate();
UriComponents uriComponents=UriComponentsBuilder.fromUriString(
"http://HELLOSERVER/hello?id={id}"
).build()
.expand("100")
.encode();
URI uri=uriComponents.toUri();
ResponseEntity
responseEntity=restTemplate.getForEntity(uri,String.class).getBody();
在上面的代码中,我们可以看到,有一个 expand 方法,添加了一个参数。在本实例中,只是简单地添加了一个参数,属于方法一中的不定参数。其实在这里还有一种方式,和方法二一样,在这里可以添加 Map 存放参数。
getForObject方式
在理解了 getForEntity 方式之后,再来理解 getForObject 方式,会比较容易。相比于 getForEntity,这种方式包含了 HTTP 转换成 pojo 的功能,通过上文说过的默认转换器 HttpMessageConverter 进行转换,将响应体 Body 的内容转换成对象,在断点调试时会发现最后看到的是一个 pojo。
这里的使用场景,因为返回的是不包含 HTTP 的其他信息,所以只关注 Body 时,非常适用。实例如下。
RestTemplate restTemplate=new RestTemplate();
User user=restTemplate.getForObject"http://HELLOSERVER/hello?id={1}",User.class,"100");
在这段代码中,我们可以看到,返回时直接获取到了 pojo,不再需要从 HttpEntity 中 getBody 这段代码。这种方式还有多个使用场景如下。
(1)public <T> T getForObject(String url, Class<T> responseType,Object… uriVariables)
这种方式对应了 getForEntity 中的方法一,使用方式也相同。这里的函数直接返回 T,即是 responseType 的类型。
(2)public <T> T getForObject(String url, Class<T> responseType,Map<String, ?>uriVariables)
这种方式对应了 getForEntity 中的方法二,使用方式也相同。
(3)public <T> T getForObject(URI url, Class<T> responseType)
这种方式对应了 getForEntity 中的方法三,使用方式也相同。
POST请求API
首先,看看 POST 的 API 有哪些,如图11.7所示。

根据上图的 API 汇总,可以分为三种方式。
postForLocation方式
在图11.7中,关于 postForLocation 的方法共有三个,而且返回参数都是资源定位 URI,这个 URI 在上文说过,是 JDK 中的类。
为了说明三个方法的不同点,我们先看看里面的代码,代码如下所示。
public URI postForLocation(String url, @Nullable Object request, Object... uriVariables)
throws RestClientException {
RequestCallback requestCallback = httpEntityCallback(request);
HttpHeaders headers = execute(url, HttpMethod.POST, requestCallback, headersExtractor(), uriVariables);
return (headers != null ? headers.getLocation() : null);
}
public URI postForLocation(String url, @Nullable Object request, Map<String, ?> uriVariables)
throws RestClientException {
RequestCallback requestCallback = httpEntityCallback(request);
HttpHeaders headers = execute(url, HttpMethod.POST, requestCallback, headersExtractor(), uriVariables);
return (headers != null ? headers.getLocation() : null);
}
public URI postForLocation(URI url, @Nullable Object request) throws RestClientException {
RequestCallback requestCallback = httpEntityCallback(request);
HttpHeaders headers = execute(url, HttpMethod.POST, requestCallback, headersExtractor());
return (headers != null ? headers.getLocation() : null);
}
从上面的代码中可以看到,每个方法中的操作几乎相同。首先会根据 request 返回一个 RequestCallback,然后执行 execute,返回 headers,最后从 headers 中取得 location。
其中,RequestCallback 允许操作请求头并写到请求体中。当使用 execute 方法时,不必担心任何资源管理,模板将总是关闭请求并处理任何错误。
这三个方法都是对给定的数据发送 POST 创建资源,返回 HTTP 头,这也就是一个新的资源。看一下 getLocation 的代码,代码如下所示。
@Nullable
public URI getLocation() {
String value = getFirst(LOCATION);
return (value != null ? URI.create(value) : null);
}
每个方法上都有如下的一段代码。
The {@code request} parameter can be a {@link HttpEntity} in order to add additional HTTP headers to the request.
这里解释了参数 Object request 的通常用法。如果想要在 HTTP headers 里面加点什么东西,可以在此处传一个 HttpEntity 对象。具体如何使用,将会在具体的使用方法上讲解。
(1)public URI postForLocation(String url, @Nullable Object request,Object… uriVariables)
在这个方法中,需要参数访问地址 url 和 uriVariables 变量。
这里有一点需要特别注意,request 可以是一个 HttpEntity,这样就可以被当作一个完整的 HTTP 请求进行处理,因为这里包含了请求头和请求体。但是这里的类型是 Object,就是说 request 也可以是一个普通的对象,然后 RestTemplate 会把请求对象转换成 HttpEntity 进行处理,request 的内容被当成一个消息体进行处理。
RestTemplate restTemplate=new RestTemplate();
HttpHeaders headers = new HttpHeaders();
// 在header里面设置编码方式
headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
String body = "tom";
HttpEntity<String> requestEntity = new HttpEntity<String>(body , headers);
URI url=restTemplate.postForLocation(URI.create("http://HELLOSERVER/hello"), requestEntity);
在上面的代码中,直接使用了 HttpEntity 对象。
(2)public URI postForLocation(String url, @Nullable Object request,Map<String, ?>uriVariables)
这里的参数保存方式使用 Map 方式,我们在前面已经介绍过,如果读者还是不清楚,可以看看 postForObject 的使用方式。
(3)public URI postForLocation(URI url, @Nullable Object request)
这个方法可以将变量的参数封装在 URI 中。
postForObject方式
对于 POST 请求方式,请求参数一般都是放在 Body 体中,这样比较安全。但是有时候,一些普通的参数也可以放在 URL 上。但在控制类 Controller 获取参数的时候,情况稍微有些不同,下面也会做一些介绍。
(1)public <T> T postForObject(String url, @Nullable Object request,Class<T>responseType,Object… uriVariables)
使用方式如下。
RestTemplate restTemplate=new RestTemplate();
Map<String, Object> object = restTemplate.postForObject("http://HELLOSERVER/hello?name={name}", null, Map.class,"tom");
上面的代码没有写 request,按照前面的说法直接写即可。然后 url 中的参数,没有放在 Body 体中,而是放在 url 上,这样直接写在最后即可。然后,关于在控制类中如何获取参数的问题,在这里做一个实例。
@PostMapping(value = "/hello")
public Map<String, Object> hello(@RequestParam String name){
Map<String, Object> ret = new HashMap();
User user= new User ();
user.setName(name);
userService.save(user);
ret.put("user", user);
return ret;
}
上面的代码比较简单,我们还可以根据 name 与 Body 体做很多事情。在 URL 挂载参数还有另一种方式,如下所示。
RestTemplate restTemplate=new RestTemplate();
Map<String, Object> object = restTemplate.postForObject("http://HELLOSERVER/hello/{name}", null, Map.class,"tom");
控制类的处理方式也稍有不同,主要是 URL 上获取参数使用的注解不同,代码如下所示。
@PostMapping(value = "/hello/{name}")
public Map<String, Object> hello2(@PathVariable String name){
Map<String, Object> ret = new HashMap();
User user= new User ();
user.setName(name);
userService.save(user);
ret.put("user", user);
return ret;
}
(2)public <T> T postForObject(String url, @Nullable Object request,Class<T>responseType,Map<String, ?> uriVariables)
这里的使用方式与上面的方式不同。这里的 URL 的参数写在了 Map 中。同样,在 URL 挂载参数的方式也有两种,这里只讲一种使用方式,代码如下所示。
Map<String,Object> requestMap = new HashMap();
requestMap.put("name", "tom");
RestTemplate restTemplate = new RestTemplate();
Map<String, Object> object = restTemplate.postForObject("http://HELLOSERVER/hello?name={name}", null, Map.class,requestMap);
这个使用方式与 GET 的方式几乎相同,将参数写在 URI 中。
在使用 Body 体时,如何在控制类中获取参数?这个问题在上文没有说明过。其实只需说明一次,其他方法的用法均相同。用法代码如下所示。
HttpHeaders headers = new HttpHeaders(); // HTTP请求头
headers.setContentType(MediaType.APPLICATION_JSON_UTF8); // 请求头设置属性
Map<String,Object> body = new HashMap(); // 请求body
body.put("name", "tom");
HttpEntity<Map<String,Object>> requestEntity = new HttpEntity<Map<String,Object>>(body,headers);
Map<String, Object> object = restTemplate.postForObject("http://HELLOSERVER/hello", requestEntity, Map.class);
然后,写一个控制类,来对应本次请求调用,代码如下所示。
@PostMapping(value = "/hello")
public Map<String, Object> hello(@RequestBody Map<String, Object>request){
Map<String, Object> ret = new HashMap();
User user= new User();
user.setName((String)request.getOrDefault("name", null));
userService.save(user);
ret.put("user", user);
return ret;
}
在上面的代码中,我们获取 Body 体需要使用 @RequestBody 注解。
postForEntity方式
这个方法与 postForObject 的区别在于,postForObject 返回的类型需要自己指定,然后返回结果就是 Body 体转换成的自己指定的类型,而 postForEntity 则包含了 HTTP 响应的 headers,Body 体等信息。这里,不具体讲解,读者可以参考 getForEntity 的使用方式。
HttpEntity
为了让读者更好地理解上面的代码,这里单独对 HttpEntity 做一个小说明。首先,直接看看构造函数,代码如下所示。
protected HttpEntity() {
this(null, null);
}
public HttpEntity(T body) {
this(body, null);
}
public HttpEntity(MultiValueMap<String, String> headers) {
this(null, headers);
}
public HttpEntity(@Nullable T body, @Nullable MultiValueMap<String, String> headers) {
this.body = body;
HttpHeaders tempHeaders = new HttpHeaders();
if (headers != null) {
tempHeaders.putAll(headers);
}
this.headers = HttpHeaders.readOnlyHttpHeaders(tempHeaders);
}
在上面的代码中,可以传递 headers,也可以传递 body,还可以同时传递 headers 和 body,可以根据自己的需要选择。
PUT请求API
PUT 的三个 API,如图11.8所示。

通过 PUT 的请求方式对资源进行创建或者更新,通过上图,我们可以看到 PUT 是不存在返回值的。然后,这个操作还是幂等性的。
使用方式与 postFor 基本相同,不过这里也看一个实例程序,代码如下所示。
RestTemplate restTemplate=new RestTemplate();
int id=100;
User user=new User("tom");
restTemplate.put("http://HELLOSERVER/hello/{id}",user,id);