一、简介
现如今的 IT 项目,由服务端向外发起网络请求的场景,基本上处处可见!
传统情况下,在服务端代码里访问 http 服务时,我们一般会使用 JDK
的 HttpURLConnection
或者 Apache
的 HttpClient
,不过这种方法使用起来太过繁琐,而且 api 使用起来非常的复杂,还得操心资源回收。
以下载文件为例,通过 Apache
的 HttpClient
方式进行下载文件,下面这个是我之前封装的代码逻辑,看看有多复杂!
其实Spring
已经为我们提供了一种简单便捷的模板类来进行操作,它就是RestTemplate
。
RestTemplate
是一个执行HTTP
请求的同步阻塞式工具类,它仅仅只是在 HTTP
客户端库(例如 JDK HttpURLConnection,Apache HttpComponents,okHttp 等)基础上,封装了更加简单易用的模板方法 API,方便程序员利用已提供的模板方法发起网络请求和处理,能很大程度上提升我们的开发效率。
好了,不多 BB 了,代码撸起来!
二、环境配置
2.1、非 Spring 环境下使用 RestTemplate
如果当前项目不是Spring
项目,加入spring-web
包,即可引入RestTemplate
类
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>5.2.6.RELEASE</version> </dependency>
编写一个单元测试类,使用RestTemplate
发送一个GET
请求,看看程序运行是否正常
@Test public void simpleTest() { RestTemplate restTemplate = new RestTemplate(); String url = "http://jsonplaceholder.typicode.com/posts/1"; String str = restTemplate.getForObject(url, String.class); System.out.println(str); }
2.2、Spring 环境下使用 RestTemplate
如果当前项目是SpringBoot
,添加如下依赖接口!
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
同时,将RestTemplate
配置初始化为一个Bean
。
@Configuration public class RestTemplateConfig { /** * 没有实例化RestTemplate时,初始化RestTemplate * @return */ @ConditionalOnMissingBean(RestTemplate.class) @Bean public RestTemplate restTemplate(){ RestTemplate restTemplate = new RestTemplate(); return restTemplate; } }
注意,这种初始化方法,是使用了JDK
自带的HttpURLConnection
作为底层HTTP
客户端实现。
当然,我们还可以修改RestTemplate
默认的客户端,例如将其改成HttpClient
客户端,方式如下:
@Configuration public class RestTemplateConfig { /** * 没有实例化RestTemplate时,初始化RestTemplate * @return */ @ConditionalOnMissingBean(RestTemplate.class) @Bean public RestTemplate restTemplate(){ RestTemplate restTemplate = new RestTemplate(getClientHttpRequestFactory()); return restTemplate; } /** * 使用HttpClient作为底层客户端 * @return */ private ClientHttpRequestFactory getClientHttpRequestFactory() { int timeout = 5000; RequestConfig config = RequestConfig.custom() .setConnectTimeout(timeout) .setConnectionRequestTimeout(timeout) .setSocketTimeout(timeout) .build(); CloseableHttpClient client = HttpClientBuilder .create() .setDefaultRequestConfig(config) .build(); return new HttpComponentsClientHttpRequestFactory(client); } }
在需要使用RestTemplate
的位置,注入并使用即可!
@Autowired private RestTemplate restTemplate;
从开发人员的反馈,和网上的各种HTTP
客户端性能以及易用程度评测来看,OkHttp
优于 Apache
的HttpClient
、Apache
的HttpClient
优于HttpURLConnection
。
因此,我们还可以通过如下方式,将底层的http
客户端换成OkHttp
!
/** * 使用OkHttpClient作为底层客户端 * @return */ private ClientHttpRequestFactory getClientHttpRequestFactory(){ OkHttpClient okHttpClient = new OkHttpClient.Builder() .connectTimeout(5, TimeUnit.SECONDS) .writeTimeout(5, TimeUnit.SECONDS) .readTimeout(5, TimeUnit.SECONDS) .build(); return new OkHttp3ClientHttpRequestFactory(okHttpClient); }
三、API 实践
RestTemplate
最大的特色就是对各种网络请求方式做了包装,能极大的简化开发人员的工作量,下面我们以GET
、POST
、PUT
、DELETE
、文件上传与下载
为例,分别介绍各个API
的使用方式!
3.1、GET 请求
通过RestTemplate
发送HTTP GET
协议请求,经常使用到的方法有两个:
- getForObject()
- getForEntity()
二者的主要区别在于,getForObject()
返回值是HTTP
协议的响应体。
getForEntity()
返回的是ResponseEntity
,ResponseEntity
是对HTTP
响应的封装,除了包含响应体,还包含HTTP
状态码、contentType
、contentLength
、Header
等信息。
在Spring Boot
环境下写一个单元测试用例,首先创建一个Api
接口,然后编写单元测试进行服务测试。
- 不带参的get请求
@RestController public class TestController { /** * 不带参的get请求 * @return */ @RequestMapping(value = "testGet", method = RequestMethod.GET) public ResponseBean testGet(){ ResponseBean result = new ResponseBean(); result.setCode("200"); result.setMsg("请求成功,方法:testGet"); return result; } }
public class ResponseBean { private String code; private String msg; public String getCode() { return code; } public void setCode(String code) { this.code = code; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } @Override public String toString() { return "ResponseBean{" + "code='" + code + '\'' + ", msg='" + msg + '\'' + '}'; } }
@Autowired private RestTemplate restTemplate; /** * 单元测试(不带参的get请求) */ @Test public void testGet(){ //请求地址 String url = "http://localhost:8080/testGet"; //发起请求,直接返回对象 ResponseBean responseBean = restTemplate.getForObject(url, ResponseBean.class); System.out.println(responseBean.toString()); }
- 带参的get请求(restful风格)
@RestController public class TestController { /** * 带参的get请求(restful风格) * @return */ @RequestMapping(value = "testGetByRestFul/{id}/{name}", method = RequestMethod.GET) public ResponseBean testGetByRestFul(@PathVariable(value = "id") String id, @PathVariable(value = "name") String name){ ResponseBean result = new ResponseBean(); result.setCode("200"); result.setMsg("请求成功,方法:testGetByRestFul,请求参数id:" + id + "请求参数name:" + name); return result; } }
@Autowired private RestTemplate restTemplate; /** * 单元测试(带参的get请求) */ @Test public void testGetByRestFul(){ //请求地址 String url = "http://localhost:8080/testGetByRestFul/{1}/{2}"; //发起请求,直接返回对象(restful风格) ResponseBean responseBean = restTemplate.getForObject(url, ResponseBean.class, "001", "张三"); System.out.println(responseBean.toString()); }
- 带参的get请求(使用占位符号传参)
@RestController public class TestController { /** * 带参的get请求(使用占位符号传参) * @return */ @RequestMapping(value = "testGetByParam", method = RequestMethod.GET) public ResponseBean testGetByParam(@RequestParam("userName") String userName, @RequestParam("userPwd") String userPwd){ ResponseBean result = new ResponseBean(); result.setCode("200"); result.setMsg("请求成功,方法:testGetByParam,请求参数userName:" + userName + ",userPwd:" + userPwd); return result; } }
@Autowired private RestTemplate restTemplate; /** * 单元测试(带参的get请求) */ @Test public void testGetByParam(){ //请求地址 String url = "http://localhost:8080/testGetByParam?userName={userName}&userPwd={userPwd}"; //请求参数 Map<String, String> uriVariables = new HashMap<>(); uriVariables.put("userName", "唐三藏"); uriVariables.put("userPwd", "123456"); //发起请求,直接返回对象(带参数请求) ResponseBean responseBean = restTemplate.getForObject(url, ResponseBean.class, uriVariables); System.out.println(responseBean.toString()); }
上面的所有的getForObject
请求传参方法,getForEntity
都可以使用,使用方法上也几乎是一致的,只是在返回结果接收的时候略有差别。