Spring Boot 提供了 RestTemplate 来辅助发起一个 REST 请求,默认通过 JDK 自带的 HttpURLConnection 来作为底层 HTTP 消息的发送方式,使用 JackSon 来序列化服务器返回的 JSON 数据。
RestTemplate 是核心类, 提供了所有访问 REST 服务的接口,尽管实际上可以使用 HTTP Client 类或者 java.net.URL来完成,但 RestTemplate 提供了阻STful 风格的 API。 Spring Boot 提供了 RestTemplateBuilder 来创建一个 RestTemplate。
RestTemplate定义11个基本操作方法,大致如下:
- delete(): 在特定的URL上对资源执行HTTP DELETE操作
- exchange(): 在URL上执行特定的HTTP方法,返回包含对象的ResponseEntity,这个对象是从响应体中
映射得到的
3.execute(): 在URL上执行特定的HTTP方法,返回一个从响应体映射得到的对象(所有的get、post、delete、put、options、head、exchange方法最终调用的都是excute方法),例如:
@Override
public <T> T getForObject(String url, Class<T> responseType, Object... urlVariables) throws RestClientException {
RequestCallback requestCallback = acceptHeaderRequestCallback(responseType); HttpMessageConverterExtractor<T> responseExtractor = <span style="white-space:pre"> </span>new HttpMessageConverterExtractor<T>(responseType, getMessageConverters(), logger); return execute(url, HttpMethod.GET, requestCallback, responseExtractor, urlVariables);
}
4.getForEntity(): 发送一个HTTP GET请求,返回的ResponseEntity包含了响应体所映射成的对象
5.getForObject() :发送一个HTTP GET请求,返回的请求体将映射为一个对象
6.postForEntity() :POST 数据到一个URL,返回包含一个对象的ResponseEntity,这个对象是从响应体中映射得
到的
7.postForObject(): POST 数据到一个URL,返回根据响应体匹配形成的对象
8.headForHeaders(): 发送HTTP HEAD请求,返回包含特定资源URL的HTTP头
9.optionsForAllow(): 发送HTTP OPTIONS请求,返回对特定URL的Allow头信息
10.postForLocation() :POST 数据到一个URL,返回新创建资源的URL
11.put(): PUT 资源到特定的URL
实际上,由于Post 操作的非幂等性,它几乎可以代替其他的CRUD操作.
一、GET请求
在RestTemplate中,发送一个GET请求,我们可以通过如下两种方式:
第一种:getForEntity
getForEntity方法的返回值是一个ResponseEntity<T>,ResponseEntity<T>是Spring对HTTP请求响应的封装,包括了几个重要的元素,如响应码、contentType、contentLength、响应消息体等。其重载方法如下:
- <T> ResponseEntity<T> getForObject(URI url, Class<T> responseType) throws RestClientException;
- <T> ResponseEntity<T> getForObject(String url, Class<T> responseType, Object... uriVariables) throws RestClientException;
- <T> ResponseEntity<T> getForObject(String url, Class<T> responseType, Map<String, ?> uriVariables) throws RestClientException;
比如下面一个例子:
@Autowired
RestTemplateBuilder restTemplateBuilder;
@RequestMapping("/gethello")
public String getHello() {
RestTemplate client = restTemplateBuilder.build ();
ResponseEntity<String> responseEntity = restTemplate.getForEntity("http://HELLO-SERVICE/hello", String.class);
String body = responseEntity.getBody();
HttpStatus statusCode = responseEntity.getStatusCode();
int statusCodeValue = responseEntity.getStatusCodeValue();
HttpHeaders headers = responseEntity.getHeaders();
StringBuffer result = new StringBuffer(); result.append("responseEntity.getBody():").append(body).append("<hr>") .append("responseEntity.getStatusCode():").append(statusCode).append("<hr>") .append("responseEntity.getStatusCodeValue():").append(statusCodeValue).append("<hr>") .append("responseEntity.getHeaders():").append(headers).append("<hr>");
return result.toString();
}
有时候我在调用服务提供者提供的接口时,可能需要传递参数,有两种不同的方式,如下:
@RequestMapping("/sayhello")
public String sayHello() {
ResponseEntity<String> responseEntity = restTemplate.getForEntity("http://HELLO-SERVICE/sayhello?name={1}", String.class, "张三");
return responseEntity.getBody();
}
@RequestMapping("/sayhello2")
public String sayHello2() {
Map<String, String> map = new HashMap<>(); map.put("name", "李四");
ResponseEntity<String> responseEntity = restTemplate.getForEntity("http://HELLO-SERVICE/sayhello?name={name}", String.class, map);
return responseEntity.getBody();
}
- 可以用一个数字做占位符,最后是一个可变长度的参数,来一一替换前面的占位符
- 也可以前面使用name={name}这种形式,最后一个参数是一个map,map的key即为前边占位符的名字,map的value为参数值
第一个调用地址也可以是一个URI而不是字符串,这个时候我们构建一个URI即可,参数神马的都包含在URI中了,如下:
@RequestMapping("/sayhello3")
public String sayHello3() {
UriComponents uriComponents = UriComponentsBuilder.fromUriString("http://HELLO-SERVICE/sayhello?name={name}").build().expand("王五").encode();
URI uri = uriComponents.toUri();
ResponseEntity<String> responseEntity = restTemplate.getForEntity(uri, String.class);
return responseEntity.getBody();
}
通过Spring中提供的UriComponents来构建Uri即可。
当然,服务提供者不仅可以返回String,也可以返回一个自定义类型的对象,比如我的服务提供者中有如下方法:
@RequestMapping(value = "/getbook1", method = RequestMethod.GET)
public Book book1() {
return new Book("三国演义", 90, "罗贯中", "花城出版社");
}
对于该方法我可以在服务消费者中通过如下方式来调用:
@RequestMapping("/book1")
public Book book1() {
ResponseEntity<Book> responseEntity = restTemplate.getForEntity("http://HELLO- SERVICE/getbook1", Book.class);
return responseEntity.getBody();
}
第二种:getForObject
getForObject函数实际上是对getForEntity函数的进一步封装,如果你只关注返回的消息体的内容,对其他信息都不关注,此时可以使用getForObject,重载方法如下:
- <T> T getForObject(URI url, Class<T> responseType) throws RestClientException;
- <T> T getForObject(String url, Class<T> responseType, Object... uriVariables) throws RestClientException;
- <T> T getForObject(String url, Class<T> responseType, Map<String, ?> uriVariables) throws RestClientException;
二、POST请求
在RestTemplate中,POST请求可以通过如下三个方法来发起:
第一种:postForEntity、postForObject
POST请求有postForObject()和postForEntity()两种方法,和GET请求的getForObject()和getForEntity()方法类似。getForLocation()是POST请求所特有的。
- <T> T postForObject(URI url, Object request, Class<T> responseType) throws RestClientException;
- <T> T postForObject(String url, Object request, Class<T> responseType, Object... uriVariables) throws RestClientException;
- <T> T postForObject(String url, Object request, Class<T> responseType, Map<String, ?> uriVariables) throws RestClientException;
上面三个方法中,第一个参数都是资源要POST到的URL,第二个参数是要发送的对象,而第三个参数是预期返回的Java类型。在URL作为String类型的两个版本中,第四个参数指定了URL变量(要么是可变参数列表,要么是一个Map)。
该方法和get请求中的getForEntity方法类似,如下例子:
@RequestMapping("/book3")
public Book book3() {
Book book = new Book();
book.setName("红楼梦");
ResponseEntity<Book> responseEntity = restTemplate.postForEntity("http://HELLO-SERVICE/getbook2", book, Book.class);
return responseEntity.getBody();
}
第二种:postForLacation
postForLacation()会在POST请求的请求体中发送一个资源到服务器端,返回的不再是资源对象,而是创建资源的位置。
- postForLocation(String url, Object request, Object... uriVariables) throws RestClientException;
- postForLocation(String url, Object request, Map<String, ?> uriVariables) throws RestClientException;
- postForLocation(URI url, Object request) throws RestClientException;
public String postSpitter(Spitter spitter) {
RestTemplate rest = new RestTemplate();
return rest.postForLocation("http://localhost:8080/Spitter/spitters", spitter).toString(); }
postForLocation也是提交新资源,提交成功之后,返回新资源的URI,postForLocation的参数和前面两种的参数基本一致,只不过该方法的返回值为Uri,这个只需要服务提供者返回一个Uri即可,该Uri表示新资源的位置。
三、PUT请求
在RestTemplate中,对PUT请求可以通过put方法进行调用实现,比如:
RestTemplate restTemplate=new RestTemplate();
Longid=100011; User user=new User("didi",40);
restTemplate.put("http://USER-SERVICE/user/{l}",user,id);
put函数也实现了三种不同的重载方法:
- put(String url,Object request,Object... urlVariables)
- put(String url,Object request,Map urlVariables)
- put(URI url,Object request)
put函数为void类型,所以没有返回内容,也就没有其他函数定义的responseType参数,除此之外的其他传入参数定义与用法与postForObject基本一致。
四、DELETE请求
在RestTemplate中,对DELETE请求可以通过delete方法进行调用实现,比如:
RestTemplate restTemplate=new RestTemplate(); Longid=10001L; restTemplate.delete("http://USER-SERVICE/user/{1)",id);
delete函数也实现了三种不同的重载方法:
- delete(String url,Object... urlVariables)
- delete(String url,Map urlVariables)
- delete(URI url)
由于我们在进行REST请求时,通常都将DELETE请求的唯一标识拼接在url中,所以DELETE请求也不需要request的body信息,就如put()方法一样,返回值类型为void。
说明:第三种重载方法,url指定DELETE请求的位置,urlVariables绑定url中的参数即可。
五、通用方法exchange()
exchange方法可以在发送个服务器端的请求中设置头信息,其重载方法如下:
- <T> ResponseEntity<T> exchange(URI url, HttpMethod method, HttpEntity<?> requestEntity, Class<T> responseType) throws RestClientException;
- <T> ResponseEntity<T> exchange(String url, HttpMethod method, HttpEntity<?> requestEntity, Class<T> responseType, Object... uriVariables) throws RestClientException;
- <T> ResponseEntity<T> exchange(String url, HttpMethod method, HttpEntity<?> requestEntity, Class<T> responseType, Map<String, ?> uriVariables) throws RestClientException;
MultiValueMap<String, String> headers = new LinkedMultiValueMap<String, String>(); headers.add("Accept", "application/json");
HttpEntity<Object> requestEntity = new HttpEntity<Object>(headers);
ResponseEntity<Spitter> response=rest.exchange("http://localhost:8080/Spitter/spitters/
{spitter}", HttpMethod.GET, requestEntity, Spitter.class, spitterId);
补充:
如果期望返回的类型是一个列表,如 List,不能简单调用 xxxForObject,因为存在泛型的类型擦除, RestTemplate 在反序列化的时候并不知道实际反序列化的类型,因此可以使用 ParameterizedTypeReference 来包含泛型类型,代码如下:
RestTemplate client= restTemplateBuilder.build();
//根据条件查询一纽订单
String uri = base+"/orders?offset={offset }";
Integer offset = 1;
//元参数
HttpEntity body = null;
ParameterizedTypeReference<List<Order> typeRef = new ParameterizedTypeReference<List<Order>(){};
ResponseEntity<List<Order> rs=client.exchange(uri, HttpMethod.GET, body, typeRef, offset); List<Order> order = rs.getBody() ;
注意到 typeRef 定义是用{}结束的,这里创建了一个 ParameterizedTypeReference 子类,依 据在类定义中的泛型信息保留的原则, typeRef保留了期望返回的泛型 List。
exchange 是一个基础的 REST 调用接口,除了需要指明 HTTP Method,调用方法同其他方法类似。
除了使用ParameterizedTypeReference子类对象,也可以先将返回结果映射成 json字符串,然后通过 ObjectMapper 来转为指定类型(笔记<SpringBoot中的JSON>中有介绍 ) 。