RestTemplate
1、简介
RestTemplate是spring3.0开始支持的一个同步的web http请求工具,Spring框架进行了抽象,提供了常见的REST请求方案的模板,例如GET、POST、PUT、DELETE请求及一些通用的请求执行方法。RestTemplate实现了RestOperations接口,并定义了基本的RESTful操作。
传统情况下,可以使用Java自带的HttpUrlConnection或者HttpClient也可以实现RESTful服务访问。
2、常见http客户端请求工具
- HttpURLConnection
- HttpClient
- okHttp
3、http状态码
| 状态码 | 描述 | |
|---|---|---|
| 1XX 信息提示,代表临时的响应 | ||
| 100-Continue | 初始的请求已经接受,客户应继续发送请求的其余部分(http1.1) | |
| 101-Switching Protocols | 服务器将遵从客户的请求转换到另一种协议(http1.1) | |
| 2XX 成功 | ||
| 200-OK | http请求成功 | |
| 201-Created | 服务器已经创建的文档 | |
| 202-Accepted | 服务器接受请求,但处理尚未完成 | |
| 3XX 重定向 | ||
| 300-MultipleChoices | 客户端请求的文档可在多个位置找到 | |
| 301-Move Permanently | 永久移动,请求的资源已经被永久的移动到新的url | |
| 302-Found | 临时移动 | |
| 303-See Other | 查看其他地址 | |
| 304-Not Modified | 未修改,所请求的资源未修改 | |
| 4XX 客户端错误 | ||
| 4000-Bad Request | 请求出现语法错误 | |
| 401-Unauthorized | 请求要求用户的身份认证 | |
| 402-Payment Required | 保留,将来使用 | |
| 403-Forbidden | 拒绝执行 | |
| 404-Not Found | 服务器无法找到请求的资源 | |
| 5XX 服务器错误 | ||
| 500-Internet Server Error | 服务器内部错误 | |
| 501-Not Implemented | 服务器不支持请求的功能 | |
| 502-Bad GateWay | 网关请求错误 | |
| 503-Service Unavailable | 服务部可用 |
这里只列出常用的http状态响应码,更多详细内容参见:www.runoob.com/http/http-s…
4、RestTemplate方法
| HTTP方法 | RESTTEMPLATE方法 | ||
|---|---|---|---|
| DELETE | delete:在特定的url上执行delete操作 | ||
| GET | getForObject:发送一个get请求,返回的请求映射为一个对象 | getForEntity:返回包含一个对象的ResponseEntity | |
| HEAD | headForHeaders:发送一个http head请求,发挥包含特定资源url的http头 | ||
| OPTIONS | optionsForAllow:返回特定的url的allow头信息 | ||
| POST | postForLocation:返回新创建资源的url | postForObject:post数据到特定的url,返回根据响应体匹配形成的对象 | postForEntity:返回一个包含对象的ResponseEntity,这个对象是响应体中映射得到 |
| PUT | put:普通资源到特定的url | ||
| 任何 | exchange:返回包含对象的ResponseEntity,对象是从响应体中得到的 | execute:在url上执行特定的http方法,返回一个从响应体映射得到的对象 |
5、RestTemplate的使用
首先我们创建一个工程,resttemplate的基本配置,使用spring boot自动注解的方式。在调用rest时,每次请求都需要和服务端建立连接,为了避免多次建立连接的操作浪费资源,我们这里采用连接池的配置。
5.1.依赖
除了spring的基本依赖外,还需要在在maven的pom.xml中添加如下依赖:
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>${httpclient.version}</version>
</dependency>
5.2.使用示例
创建resttemplate配置类:
package com.crystal.master.rest;
import org.apache.http.Header;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.impl.client.DefaultConnectionKeepAliveStrategy;
import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicHeader;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
import java.util.ArrayList;
import java.util.List;
@Configuration
public class RestConfig {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate(clientHttpRequestFactory());
}
@Bean
public ClientHttpRequestFactory clientHttpRequestFactory() {
return new HttpComponentsClientHttpRequestFactory(httpClient());
}
@Bean
public HttpClient httpClient() {
// 配置http连接
PoolingHttpClientConnectionManager poolingHttpClientConnectionManager = new PoolingHttpClientConnectionManager();
// 配置连接池最大连接数
poolingHttpClientConnectionManager.setMaxTotal(50);
// 最大同路由并发数
poolingHttpClientConnectionManager.setDefaultMaxPerRoute(500);
// requestConfig
RequestConfig requestConfig = RequestConfig.custom()
// 服务器返回数据的时间
.setSocketTimeout(10000)
// 连接服务器(握手成功)的时间,超出时间抛出异常
.setConnectTimeout(5000)
// 连接池获取连接的时间
.setConnectionRequestTimeout(500)
.build();
// 设置请求头
List<Header> headers = new ArrayList();
headers.add(new BasicHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.16 Safari/537.36"));
headers.add(new BasicHeader("Accept-Encoding", "gzip,deflate"));
headers.add(new BasicHeader("Accept-Language", "zh-CN"));
headers.add(new BasicHeader("Connection", "keep-alive"));
headers.add(new BasicHeader("Content-type", "application/json,charset=UTF-8"));
return HttpClientBuilder.create()
.setDefaultRequestConfig(requestConfig)
.setConnectionManager(poolingHttpClientConnectionManager)
.setDefaultHeaders(headers)
// 保持长连接,需要在头添加keep-alive
.setKeepAliveStrategy(new DefaultConnectionKeepAliveStrategy())
// 开始重试
.setRetryHandler(new DefaultHttpRequestRetryHandler(2, true))
.build();
}
}
5.3.GET请求
做好配置工作后,先来看下使用RestTemplate发送get请求,get请求相关的主要方法有getForObject和getForEntity。下面我们分别模拟使用
getForEntity: resttemplate发送的http请求,那么响应的数据中必然也存在响应头,如果开发者需要获取响应头信息的话,那么需要使用getForEntity,此时返回的是ResponseEntity的实例。
在创建的好的工程中,创建一个RestTemplateController,在RestTemplateController中创建getForEntity方法,如下:
package com.crystal.master.controller;
import com.crystal.common.dto.UserDTO;
import io.swagger.annotations.Api;
import org.springframework.context.annotation.Lazy;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
@Api(value = "restful")
@RestController
@RequestMapping("/rest")
public class RestTemplateController {
@Resource
@Lazy
private RestTemplate restTemplate;
@RequestMapping(value = "/getForEntity", method = { RequestMethod.GET })
public UserDTO getForEntity() {
ResponseEntity<UserDTO> responseEntity = restTemplate.getForEntity("http://localhost:8080/welcome/hello", UserDTO.class);
return responseEntity.getBody();
}
}
启动项目,在浏览器中输入http://localshot:8080/rest/getForEntity,在return resonseEntity.getBody()处设置断点,看到如下信息:
继续执行,可以看到浏览器返回结果:
5.4.POST请求
和get方法相比,post请求的方法除了postForEntity和postForObject外,还有一个postForLocation.
postForEntity: 在post请求中,参数的传递可以是key/value形式,也可以是json格式。 @RequestMapping(value = "/postForEntity", method = { RequestMethod.GET }) public String postForEntity(String name) { MultiValueMap map = new LinkedMultiValueMap<>(); map.add("name", name); ResponseEntity responseEntity = restTemplate.postForEntity("http://localhost:8080/welcome/postForEntity", map, String.class); return responseEntity.getBody(); }
创建一个UserController来测试。
@RequestMapping(value = "/postForEntity", method = { RequestMethod.POST })
public String postForEntity(String name) {
return "hello, " + name;
}
json格式:在公共common项目中新建一个UserDTO对象,这里只需把传来的userdto对象返回回去即可。
@RequestMapping(value = "/postForEntityJson", method = { RequestMethod.GET })
public UserDTO postForEntityJson() {
UserDTO userDTO = new UserDTO();
userDTO.setUserName("crystal");
userDTO.setAge(18);
ResponseEntity<UserDTO> responseEntity = restTemplate.postForEntity("http://localhost:8080/welcome/postForEntityJson", userDTO, UserDTO.class);
return responseEntity.getBody();
}
在UserController中新建一个接收参数为userDTO的方法测试postForEntity方法。
@RequestMapping(value = "/postForEntityJson", method = { RequestMethod.POST })
public UserDTO postForEntityJson(@RequestBody UserDTO userDTO) {
return userDTO;
}
postForObject: 和postForEntity基本一致,返回类型不同,这里不在赘述。
5.5.exchange
RestTemplate的一个通用的方法,这个方法需要在调用的时候指定请求类型,既能做get请求,也可以做post请求,或者其他请求类型(HttpMethod枚举类)。
@RequestMapping(value = "/exchange", method = { RequestMethod.GET })
public String exchange() {
// 使用exchange时可以指定请求头
HttpHeaders headers = new HttpHeaders();
HttpEntity<MultiValueMap<String, String>> httpEntity = new HttpEntity<>(null, headers);
ResponseEntity<String> responseEntity = restTemplate.exchange("http://localhost:8080/welcome/hello", HttpMethod.GET, null, String.class);
return responseEntity.getBody();
}
这里的参数和get和post中的差不多,注意参数中多了个请求类型的参数HttpMethod.GET,其中HttpEntity创建时需要传递两个参数,示例中我们第一个参数设置了null,这个参数相当于post中的第二个参数。HttpEntity中第二个参数就是请求头了,可以根据自己实际情况指定请求头。
5.6.execute
查看源码可知,所有的get、post、delete等方法最终都是调用的execute方法。
- public T execute(String url, HttpMethod method, @Nullable RequestCallback requestCallback, @Nullable ResponseExtractor responesExtractor, Object ... uriVariables)
@Nullable public <T> T execute(String url, HttpMethod method, @Nullable RequestCallback requestCallback, @Nullable ResponseExtractor<T> responseExtractor, Object... uriVariables) throws RestClientException { URI expanded = this.getUriTemplateHandler().expand(url, uriVariables); return this.doExecute(expanded, method, requestCallback, responseExtractor); } - public T execute(String url, HttpMethod method, @Nullable RequestCallback requestCallback, @Nullable ResponseExtractor responesExtractor, Map(<String, ?> uriVariables)
从上面的源码可以看出,execute是将String格式的uri转换成java.net.URI,之后调用doExecute方法。@Nullable public <T> T execute(String url, HttpMethod method, @Nullable RequestCallback requestCallback, @Nullable ResponseExtractor<T> responseExtractor, Map<String, ?> uriVariables) throws RestClientException { URI expanded = this.getUriTemplateHandler().expand(url, uriVariables); return this.doExecute(expanded, method, requestCallback, responseExtractor); }
5.7.其他
RestTemplate中还有一些其他方法,put、delete、postForLocation 等方法,具体实际使用的方法我们就不在这里一一展示了,需要的可以查看RestTemplate自行学习。
总结:本篇主要介绍了RestTemplate这样一个http请求工具的基本方法,文中未涉及的其他一些方法,有兴趣的小伙伴可以自行查阅资料学习使用。在SpringMVC中支持良好的RESTful风格接口开发,RestTemplate在设计中,也提供了很好的RESTful请求封装,掌握这个基本工具的使用是必要的。