使用Spring MVC创建REST API
四、REST 客户端——细读RestTemplate
RestTemplate概述
spring框架提供的RestTemplate类可用于在应用中调用rest服务,它简化了与http服务的通信方式,统一了RESTful的标准,封装了http链接, 我们只需要传入url及返回值类型即可。相较于之前常用的HttpClient,RestTemplate是一种更优雅的调用RESTful服务的方式。
在Spring应用程序中访问第三方REST服务与使用Spring RestTemplate类有关。
RestTemplate类的设计原则与许多其他Spring *模板类(例如JdbcTemplate、JmsTemplate)相同,为执行复杂任务提供了一种具有默认行为的简化方法。
RestTemplate默认依赖JDK提供http连接的能力(HttpURLConnection),如果有需要的话也可以通过setRequestFactory方法替换为例如 Apache HttpComponents、Netty或OkHttp等其它HTTP library。
根据源码结构
RestTemplate类中定义了大约四十个与 REST 资源交互的方法,其中大多数都对应于HTTP的方法。但是仔细分析,里面只有十二种方法。其余都是这12种方法的重载形式。下表描述了RestTemplate的12个独立方法。
序号 | 方法 | 重载方法数 | 描述 |
---|---|---|---|
1 | delete() | 3 | 在特定的 URL 上对资源执行 HTTP DELETE 操作 |
2 | exchange() | 8 | 在 URL上执行特定的 HTTP 方法,返回包含对象的 ResponseEntity,这个对象是在响应体中映射得到的 |
3 | excute() | 3 | 在 URL上执行特定的 HTTP 方法,返回从响应体中映射得到的对象 |
4 | getForObject() | 3 | 发送一个 HTTP GET请求,返回的 请求体将映射成一个对象 |
5 | getForEntity() | 3 | 发送一个 HTTP GET请求,返回的 ResponseEntity包含了响应体所映射成的对象 |
6 | HeadForHeaders() | 3 | 发送一个 HTTP HEAD请求,返回包含特定资源URL的HTTP头 |
7 | optionsForAllow() | 3 | 发送一个 HTTP OPTIONS请求,返回对特定URL的Allow头信息 |
8 | postForObject() | 3 | POST数据到一个URL,返回根据响应体匹配形成的对象 |
9 | postForEntity() | 3 | POST数据到一个URL,返回包含一个对象的ResponseEntiry,这个对象是从响应体中映射得到的 |
10 | postForLocation() | 3 | POST数据到一个URL,返回新创建资源的URL |
11 | put() | 3 | PUT资源到一个URL |
12 | patchForObject() | 3 | PATCH数据到一个URL,返回根据响应体匹配形成的对象 |
简单分析源码可以看出以下几个特点
-
除了TRACE意外,RestTemplate涵盖了所有的HTTP动作。除此之外,exchange()和excute()提供了较低层次的通用方法来使用任意的HTP方法,特定的方法其实最后都是走的
excete()
方法; -
大多数操作都是以三种方式进行了重载:
- 使用java.net.URI作为URL的形式,不支持参数化URI;
- 使用String作为URL格式,并使用Map指明URL参数;
- 使用Strig作为URL格式,并使用可变擦书列表指明URL参数。
-
RestTemplate包含以下几个部分:
- HttpMessageConverter 对象转换器
- ClientHttpRequestFactory 默认是JDK的HttpURLConnection
- ResponseErrorHandler 异常处理
- ClientHttpRequestInterceptor 请求拦截器
-
api位于
org.springframework.web.client
包下 -
xxForObject()与xxForEntity()方法区别与联系
- 返回类型不同,xxForObject直接返回匹配的,而xxForEntity()方法返回一个ResponseEntity
对象。该对象的body里存着映射成的对象,除此之外还有本次响应相关的额外信息,如状态码、HttpHeader信息等。 - 参数类型是一致的
- 返回类型不同,xxForObject直接返回匹配的,而xxForEntity()方法返回一个ResponseEntity
RestTemplate官方 最新 API
使用示例
Get
源码接口:
<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;
<T> T getForObject(URI url, Class<T> responseType) throws RestClientException;
<T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Object... uriVariables)
throws RestClientException;
<T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Map<String, ?> uriVariables)
throws RestClientException;
<T> ResponseEntity<T> getForEntity(URI url, Class<T> responseType) throws RestClientException;
-
简单查询,uri里不带参数
public static void testGet1() { final String uri = "http://localhost:8080/students"; RestTemplate restTemplate = new RestTemplate(); String object = restTemplate.getForObject(uri, String.class); System.out.println(object); }
-
url参数
public static void testGet2() { String uri = "http://localost:8080/student/{studentId}"; RestTemplate restTemplate = new RestTemplate(); int studentId = 1; Student student = restTemplate.getForObject(uri, Student.class, studentId); System.out.println(student); }
public static void testGet3() { String uri = "http://localost:8080/student/{studentId}"; RestTemplate restTemplate = new RestTemplate(); int studentId = 1; Map<String, Integer> params = new HashMap<String, Integer>(); params.put("studentId", studentId); Student student = restTemplate.getForObject(uri, Student.class, params); System.out.println(student); }
-
如果GET方法请求参数需要添加认证、Header头信息等,则需要使用exchange()方法,见下文。
Post
Put
Patch
Head
Options
Delete
exchange()
上文中提到,xxForObject()方法与xxForEntity()方法的区别是后者可以同时返回对象和响应头、状态码。能在响应中读取到这头信息是很有用的。但是如果想在发送给服务器的请求中设置头信息的话,怎么办呢?这就是RestTemplate的exchange()的用武之地。
exchange()的 8 个接口 源码:
<T> ResponseEntity<T> exchange(String url, HttpMethod method, @Nullable HttpEntity<?> requestEntity,
Class<T> responseType, Object... uriVariables) throws RestClientException;
<T> ResponseEntity<T> exchange(String url, HttpMethod method, @Nullable HttpEntity<?> requestEntity,
Class<T> responseType, Map<String, ?> uriVariables) throws RestClientException;
<T> ResponseEntity<T> exchange(URI url, HttpMethod method, @Nullable HttpEntity<?> requestEntity,
Class<T> responseType) throws RestClientException;
<T> ResponseEntity<T> exchange(String url,HttpMethod method, @Nullable HttpEntity<?> requestEntity,
ParameterizedTypeReference<T> responseType, Object... uriVariables) throws RestClientException;
<T> ResponseEntity<T> exchange(String url, HttpMethod method, @Nullable HttpEntity<?> requestEntity,
ParameterizedTypeReference<T> responseType, Map<String, ?> uriVariables) throws RestClientException;
<T> ResponseEntity<T> exchange(URI url, HttpMethod method, @Nullable HttpEntity<?> requestEntity,
ParameterizedTypeReference<T> responseType) throws RestClientException;
<T> ResponseEntity<T> exchange(RequestEntity<?> requestEntity, Class<T> responseType)
throws RestClientException;
<T> ResponseEntity<T> exchange(RequestEntity<?> requestEntity, ParameterizedTypeReference<T> responseType)
throws RestClientException;
exchange()方法使用HttpMethod参数来表明要使用的HTTP动作。根据此参数,该方法可以与上文具体的HTTP方法一样的工作。
public void testGet4() {
HttpHeaders headers = new HttpHeaders();
headers.set("kbn-xsrf", "true");
headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
// 手动添加认证
String userName = "root";
String password = "123456";
String str = userName + ":" + password;
String auth = "Basic " + Base64GeneratorUtil.str2Base64(str);
headers.set("Authorization", auth);
// 如果需要 还可以在这里添加一些json字符串信息
String jsonObj = "{}";
HttpEntity<String> request = new HttpEntity<>(jsonObj, headers);
String uri = "http://localost:8080/student/{studentId}";
RestTemplate restTemplate = new RestTemplate();
int studentId = 1;
ResponseEntity<Student> entity = restTemplate.exchange(uri, HttpMethod.PUT, request, Student.class, studentId);
Student student = entity.getBody();
HttpStatus statusCode = entity.getStatusCode();
HttpHeaders headers1 = entity.getHeaders();
}
excute()
略。