本文介绍 Spring 中特别厉害的一个类 RestTemplate。
发送 http 请求,估计很多人用过 httpclient 和 okhttp,确实挺好用的,而 Spring web 中的 RestTemplate 和这
俩的功能类似,也是用来发送 http 请求的,不过用法上面比前面的 2 位要容易很多。
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 服务而设计的,因此它的主要方法与 REST 的基础紧密相连就不足为奇
了,后者是 HTTP 协议的方法:HEAD、GET、POST、PUT、DELETE 和 OPTIONS。例如,RestTemplate 类具有
headForHeaders()、getForObject()、postForObject()、put()和 delete()等方法。
下面给大家上案例,案例是重点,通过案例,把我知道的用法都给盘出来。
pom依赖:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.5.3version>
<relativePath/>
parent>
<groupId>com.resttemplategroupId>
<artifactId>spring-boot-resttemplateartifactId>
<version>0.0.1-SNAPSHOTversion>
<name>spring-boot-resttemplatename>
<description>spring-boot-resttemplatedescription>
<properties>
<java.version>1.8java.version>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<scope>testscope>
dependency>
<dependency>
<groupId>org.apache.httpcomponentsgroupId>
<artifactId>httpclientartifactId>
<version>4.5.7version>
dependency>
<dependency>
<groupId>commons-fileuploadgroupId>
<artifactId>commons-fileuploadartifactId>
<version>1.4version>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
<configuration>
<fork>truefork>
configuration>
plugin>
plugins>
build>
project>
接口代码:
@GetMapping("/test/get")
@ResponseBody
public BookDto get() {
return new BookDto(1, "SpringMVC系列");
}
package com.example.dto;
/**
* @author tom
*/
public class BookDto {
private Integer id;
private String name;
public BookDto() {
}
public BookDto(Integer id, String name) {
this.id = id;
this.name = name;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "BookDto{" + "id=" + id + ", name='" + name + '\'' + '}';
}
}
使用 RestTemplate
调用上面这个接口,通常有 2 种写法,如下:
@Test
public void test1() {
RestTemplate restTemplate = new RestTemplate();
String url = "http://localhost:8080/test/get";
// getForObject方法,获取响应体,将其转换为第二个参数指定的类型
BookDto bookDto = restTemplate.getForObject(url, BookDto.class);
// BookDto{id=1, name='SpringMVC系列'}
System.out.println(bookDto);
}
@Test
public void test2() {
RestTemplate restTemplate = new RestTemplate();
String url = "http://localhost:8080/test/get";
// getForEntity方法,返回值为ResponseEntity类型
// ResponseEntity中包含了响应结果中的所有信息,比如头、状态、body
ResponseEntity<BookDto> responseEntity = restTemplate.getForEntity(url, BookDto.class);
// 状态码
// 200 OK
System.out.println(responseEntity.getStatusCode());
// 获取头
// [Content-Type:"application/json", Transfer-Encoding:"chunked", Date:"Sat, 27 Jan 2024 02:00:47 GMT", Keep-Alive:"timeout=60", Connection:"keep-alive"]
System.out.println(responseEntity.getHeaders());
// 获取body
BookDto bookDto = responseEntity.getBody();
// BookDto{id=1, name='SpringMVC系列'}
System.out.println(bookDto);
}
接口代码:
@GetMapping("/test/get/{id}/{name}")
@ResponseBody
public BookDto get(@PathVariable("id") Integer id, @PathVariable("name") String name) {
return new BookDto(id, name);
}
使用 RestTemplate
调用上面这个接口,通常有 2 种写法,如下:
@Test
public void test3() {
RestTemplate restTemplate = new RestTemplate();
// url中有动态参数
String url = "http://localhost:8080/test/get/{id}/{name}";
Map<String, String> uriVariables = new HashMap<>();
uriVariables.put("id", "1");
uriVariables.put("name", "SpringMVC系列");
// 使用getForObject或者getForEntity方法
BookDto bookDto = restTemplate.getForObject(url, BookDto.class, uriVariables);
// BookDto{id=1, name='SpringMVC系列'}
System.out.println(bookDto);
}
@Test
public void test4() {
RestTemplate restTemplate = new RestTemplate();
// url中有动态参数
String url = "http://localhost:8080/test/get/{id}/{name}";
Map<String, String> uriVariables = new HashMap<>();
uriVariables.put("id", "1");
uriVariables.put("name", "SpringMVC系列");
// getForEntity方法
ResponseEntity<BookDto> responseEntity = restTemplate.getForEntity(url, BookDto.class, uriVariables);
BookDto bookDto = responseEntity.getBody();
// BookDto{id=1, name='SpringMVC系列'}
System.out.println(bookDto);
}
接口代码:
@GetMapping("/test/getList")
@ResponseBody
public List<BookDto> getList() {
return Arrays.asList(new BookDto(1, "Spring高手系列"), new BookDto(2, "SpringMVC系列"));
}
当接口的返回值为泛型的时候,这种情况比较特殊,使用 RestTemplate 调用上面这个接口,代码如下,需要用到
restTemplate.exchange
的方法,这个方法中有个参数是ParameterizedTypeReference
类型,通过这个参数
类指定泛型类型。
@Test
public void test5() {
RestTemplate restTemplate = new RestTemplate();
// 返回值为泛型
String url = "http://localhost:8080/test/getList";
// 若返回结果是泛型类型的,需要使用到exchange方法
// 这个方法中有个参数是ParameterizedTypeReference类型,通过这个参数类指定泛型类型
ResponseEntity<List<BookDto>> responseEntity = restTemplate.exchange(url, HttpMethod.GET, null, new ParameterizedTypeReference<List<BookDto>>() {
});
List<BookDto> bookDtoList = responseEntity.getBody();
// [BookDto{id=1, name='Spring高手系列'}, BookDto{id=2, name='SpringMVC系列'}]
System.out.println(bookDtoList);
}
接口代码如下,这个接口会下载服务器端的 1.txt 文件。
/**
* 下载文件
*
* @return
*/
@GetMapping("/test/downFile")
@ResponseBody
public HttpEntity<InputStreamResource> downFile() {
// 将文件流封装为InputStreamResource对象
InputStream inputStream = this.getClass().getResourceAsStream("/1.txt");
InputStreamResource inputStreamResource = new InputStreamResource(inputStream);
//设置header
MultiValueMap<String, String> headers = new HttpHeaders();
headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=1.txt");
HttpEntity<InputStreamResource> httpEntity = new HttpEntity<>(inputStreamResource);
return httpEntity;
}
使用 RestTemplate 调用这个接口,代码如下,目前这个文件的内容比较少,可以直接得到一个数组。
@Test
public void test6() {
RestTemplate restTemplate = new RestTemplate();
String url = "http://localhost:8080/test/downFile";
// 文件比较小的情况,直接返回字节数组
ResponseEntity<byte[]> responseEntity = restTemplate.getForEntity(url, byte[].class);
// 获取文件的内容
byte[] body = responseEntity.getBody();
String content = new String(body);
// aaaaaa
System.out.println(content);
}
注意:如果文件大的时候,这种方式就有问题了,会导致 oom,要用下面的方式了。
接口代码继续使用上面下载 1.txt 的代码。
文件比较大的时候,比如好几个 G,就不能返回字节数组了,会把内存撑爆,导致 OOM,需要使用 execute
方法了,这个方法中有个 ResponseExtractor 类型的参数,restTemplate 拿到结果之后,会回调{@link
ResponseExtractor#extractData}这个方法,在这个方法中可以拿到响应流,然后进行处理,这个过程就是
变读边处理,不会导致内存溢出。
此时使用 RestTemplate 调用这个接口,代码如下:
@Test
public void test7() {
RestTemplate restTemplate = new RestTemplate();
String url = "http://localhost:8080/test/downFile";
String result = restTemplate.execute(url, HttpMethod.GET, null, new ResponseExtractor<String>() {
@Override
public String extractData(ClientHttpResponse response) throws IOException {
// 状态:200 OK
System.out.println("状态:" + response.getStatusCode());
// Headers:[Content-Type:"text/html", Transfer-Encoding:"chunked", Date:"Sat, 27 Jan 2024 02:31:03 GMT", Keep-Alive:"timeout=60", Connection:"keep-alive"]
System.out.println("Headers:" + response.getHeaders());
// 获取响应体流
InputStream body = response.getBody();
// 处理响应体流
String content = IOUtils.toString(body, "UTF-8");
return content;
}
}, new HashMap<>());
// aaaaaa
System.out.println(result);
}
接口代码:
@GetMapping("/test/header")
@ResponseBody
public Map<String, List<String>> header(HttpServletRequest request) {
Map<String, List<String>> header = new LinkedHashMap<>();
Enumeration<String> headerNames = request.getHeaderNames();
while (headerNames.hasMoreElements()) {
String name = headerNames.nextElement();
Enumeration<String> values = request.getHeaders(name);
List<String> list = new ArrayList<>();
while (values.hasMoreElements()) {
list.add(values.nextElement());
}
header.put(name, list);
}
return header;
}
使用 RestTemplate 调用接口,请求头中传递数据,用到了HttpHeaders
和RequestEntity
,这两处是关键。
请求头放在 HttpHeaders 对象中。
RequestEntity:请求实体,请求的所有信息都可以放在 RequestEntity 中,比如 body 部分、头、请求方
式、url 等信息。
@Test
public void test8() {
RestTemplate restTemplate = new RestTemplate();
String url = "http://localhost:8080/test/header";
// 请求头放在HttpHeaders对象中
MultiValueMap<String, String> headers = new HttpHeaders();
headers.add("header-1", "V1");
headers.add("header-2", "Spring");
headers.add("header-2", "SpringBoot");
// RequestEntity,请求实体,请求的所有信息都可以放在RequestEntity中,比如body部分、头、请求方式、url等信息
RequestEntity requestEntity = new RequestEntity(null, //body部分数据
headers, //头
HttpMethod.GET,//请求方法
URI.create(url) //地址
);
ResponseEntity<Map<String, List<String>>> responseEntity = restTemplate.exchange(requestEntity, new ParameterizedTypeReference<Map<String, List<String>>>() {
});
Map<String, List<String>> result = responseEntity.getBody();
// {accept=[application/json, application/*+json], header-1=[V1], header-2=[Spring, SpringBoot], user-agent=[Java/1.8.0_77], host=[localhost:8080], connection=[keep-alive]}
System.out.println(result);
}
接口:
@GetMapping("/test/getAll/{path1}/{path2}")
@ResponseBody
public Map<String, Object> getAll(@PathVariable("path1") String path1, @PathVariable("path2") String path2, HttpServletRequest request) {
Map<String, Object> result = new LinkedHashMap<>();
result.put("path1", path1);
result.put("path2", path2);
// 头
Map<String, List<String>> header = new LinkedHashMap<>();
Enumeration<String> headerNames = request.getHeaderNames();
while (headerNames.hasMoreElements()) {
String name = headerNames.nextElement();
Enumeration<String> values = request.getHeaders(name);
List<String> list = new ArrayList<>();
while (values.hasMoreElements()) {
list.add(values.nextElement());
}
header.put(name, list);
}
result.put("header", header);
return result;
}
如下,使用 RestTemplate 调用接口,GET 方式、传递 header、path 中动态参数。
@Test
public void test9() {
RestTemplate restTemplate = new RestTemplate();
String url = "http://localhost:8080/test/getAll/{path1}/{path2}";
// 1、请求头
MultiValueMap<String, String> headers = new HttpHeaders();
headers.add("header-1", "V1");
headers.add("header-2", "Spring");
headers.add("header-2", "SpringBoot");
// 2、url中的2个参数
Map<String, String> uriVariables = new HashMap<>();
uriVariables.put("path1", "v1");
uriVariables.put("path2", "v2");
// 3、HttpEntity,HTTP实体,内部包含了请求头和请求体
HttpEntity requestEntity = new HttpEntity(null,//body部分,get请求没有body,所以为null
headers //头
);
// 4、使用exchange发送请求
ResponseEntity<Map<String, Object>> responseEntity = restTemplate.exchange(url, //url
HttpMethod.GET, //请求方式
requestEntity, //请求实体(头、body)
new ParameterizedTypeReference<Map<String, Object>>() {
},//返回的结果类型
uriVariables //url中的占位符对应的值
);
Map<String, Object> result = responseEntity.getBody();
// {path1=v1, path2=v2, header={accept=[application/json, application/*+json], header-1=[V1], header-2=[Spring, SpringBoot], user-agent=[Java/1.8.0_77], host=[localhost:8080], connection=[keep-alive]}}
System.out.println(result);
}
http 请求头中的 Content-Type 用来指定请求的类型,常见的有 3 种:
Content-Type | 说明 |
---|---|
application/x-www-form-urlencoded |
页面中普通的 form 表单提交时就是这种类型,表单中的元素会按照名称和值拼接好,然后之间用&连接,格式如:p1=v1&p2=v2&p3=v3 然后通过 urlencoded 编码之后丢在 body 中发送 |
multipart/form-data |
页面中表单上传文件的时候,用到的就是这种格式 |
application/json |
将发送的数据转换为 json 格式,丢在 http 请求的 body 中发送,后端接口通常用@RequestBody 配合对象来接收。 |
下面看这种方式的案例。
普通表单默认为 application/x-www-form-urlencoded
类型的请求。
接口代码:
@PostMapping("/test/form1")
@ResponseBody
public BookDto form1(BookDto bookDto) {
return bookDto;
}
使用 RestTemplate 调用接口:
@Test
public void test10() {
RestTemplate restTemplate = new RestTemplate();
String url = "http://localhost:8080/test/form1";
// 表单信息,需要放在MultiValueMap中,MultiValueMap相当于Map>
MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
// 调用add方法填充表单数据(表单名称:值)
body.add("id", "1");
body.add("name", "SpringMVC系列");
// 发送请求(url,请求体,返回值需要转换的类型)
BookDto result = restTemplate.postForObject(url, body, BookDto.class);
// BookDto{id=1, name='SpringMVC系列'}
System.out.println(result);
}
如果想携带头信息,代码如下:
@Test
public void test11() {
RestTemplate restTemplate = new RestTemplate();
String url = "http://localhost:8080/test/form1";
// 表单信息,需要放在MultiValueMap中,MultiValueMap相当于Map>
MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
// 调用add方法放入表单元素(表单名称:值)
body.add("id", "1");
body.add("name", "SpringMVC系列");
// 请求头
HttpHeaders headers = new HttpHeaders();
// 调用set方法放入请求头
headers.set(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE);
// 请求实体,包含了请求体和请求头
HttpEntity<MultiValueMap<String, String>> httpEntity = new HttpEntity<>(body, headers);
// 发送请求(url,请求实体,返回值需要转换的类型)
BookDto result = restTemplate.postForObject(url, httpEntity, BookDto.class);
// BookDto{id=1, name='SpringMVC系列'}
System.out.println(result);
}
上传文件 Content-Type 为 multipart/form-data
类型。
接口如下,上传上传单个文件,返回值为一个 Map 类型,是泛型类型。
@PostMapping(value = "/test/form2")
@ResponseBody
public Map<String, String> form2(@RequestParam("file1") MultipartFile file1) {
Map<String, String> fileMetadata = new LinkedHashMap<>();
fileMetadata.put("文件名", file1.getOriginalFilename());
fileMetadata.put("文件类型", file1.getContentType());
fileMetadata.put("文件大小(byte)", String.valueOf(file1.getSize()));
return fileMetadata;
}
使用 RestTemplate 调用接口,主要下面代码上传的文件需要包装为
org.springframework.core.io.Resource
,常用的有 3 种:
[FileSystemResource、InputStreamResource、ByteArrayResource]
这里案例中我们用到的是 FileSystemResource 来上传本地文件,另外 2 种(InputStreamResource、
ByteArrayResource)用法就比较特殊了,见下个案例。
@Test
public void test12() {
RestTemplate restTemplate = new RestTemplate();
String url = "http://localhost:8080/test/form2";
// 1、表单信息,需要放在MultiValueMap中,MultiValueMap相当于Map>
MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
// 调用add方法放入表单元素(表单名称:值)
// 2、文件对应的类型,需要是org.springframework.core.io.Resource类型的,常见的有[InputStreamResource,ByteArrayResource]
body.add("file1", new FileSystemResource(".\\src\\main\\java\\com\\example\\dto\\UserDto.java"));
// 3、头
HttpHeaders headers = new HttpHeaders();
headers.add("header1", "v1");
headers.add("header2", "v2");
// 4、请求实体
RequestEntity<MultiValueMap<String, Object>> requestEntity = new RequestEntity<>(body, headers, HttpMethod.POST, URI.create(url));
// 5、发送请求(请求实体,返回值需要转换的类型)
ResponseEntity<Map<String, String>> responseEntity = restTemplate.exchange(requestEntity, new ParameterizedTypeReference<Map<String, String>>() {
});
Map<String, String> result = responseEntity.getBody();
// {文件名=UserDto.java, 文件类型=text/x-java-source, 文件大小(byte)=761}
System.out.println(result);
}
有时候,上传的文件是通过流的方式或者字节数组的方式,那么就需要用到 InputStreamResource、
ByteArrayResource 这俩了。
注意:使用这俩的时候,需要重写 2 个方法,否则会上传失败:
getFilename:文件名称。
contentLength:长度。
@Test
public void test13() {
RestTemplate restTemplate = new RestTemplate();
String url = "http://localhost:8080/test/form2";
// 1、表单信息,需要放在MultiValueMap中,MultiValueMap相当于Map>
MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
/**
* 2、通过流的方式上传文件,流的方式需要用到InputStreamResource类,需要重写2个方法
* getFilename:文件名称
* contentLength:长度
*/
InputStream inputStream = RestTemplateTest.class.getResourceAsStream("/1.txt");
InputStreamResource inputStreamResource = new InputStreamResource(inputStream) {
@Override
public String getFilename() {
return "1.txt";
}
@Override
public long contentLength() throws IOException {
return inputStream.available();
}
};
body.add("file1", inputStreamResource);
// 3、头
HttpHeaders headers = new HttpHeaders();
headers.add("header1", "v1");
headers.add("header2", "v2");
// 4、请求实体
RequestEntity<MultiValueMap<String, Object>> requestEntity = new RequestEntity<>(body, headers, HttpMethod.POST, URI.create(url));
// 5、发送请求(请求实体,返回值需要转换的类型)
ResponseEntity<Map<String, String>> responseEntity = restTemplate.exchange(requestEntity, new ParameterizedTypeReference<Map<String, String>>() {
});
Map<String, String> result = responseEntity.getBody();
// {文件名=1.txt, 文件类型=text/plain, 文件大小(byte)=6}
System.out.println(result);
}
接口:
/**
* 复杂的表单:包含了普通元素、多文件
*
* @param userDto
* @return
*/
@PostMapping("/test/form3")
@ResponseBody
public Map<String, String> form3(UserDto userDto) {
Map<String, String> result = new LinkedHashMap<>();
result.put("name", userDto.getName());
result.put("headImg", userDto.getHeadImg().getOriginalFilename());
result.put("idImgList", Arrays.toString(userDto.getIdImgList().stream().map(MultipartFile::getOriginalFilename).toArray()));
return result;
}
UserDto:包含了多个元素(姓名、头像、多张证件照),这种可以模拟复杂的表单。
package com.example.dto;
import org.springframework.web.multipart.MultipartFile;
import java.util.List;
/**
* @author tom
*/
public class UserDto {
// 姓名
private String name;
// 头像
private MultipartFile headImg;
// 多张证件照
private List<MultipartFile> idImgList;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public MultipartFile getHeadImg() {
return headImg;
}
public void setHeadImg(MultipartFile headImg) {
this.headImg = headImg;
}
public List<MultipartFile> getIdImgList() {
return idImgList;
}
public void setIdImgList(List<MultipartFile> idImgList) {
this.idImgList = idImgList;
}
}
用 RestTemplate 调用这个接口,代码如下:
@Test
public void test14() {
RestTemplate restTemplate = new RestTemplate();
String url = "http://localhost:8080/test/form3";
// 1、表单信息,需要放在MultiValueMap中,MultiValueMap相当于Map>
MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
body.add("name", "路人");
body.add("headImg", new FileSystemResource(".\\src\\main\\resources\\1.jpg"));
// 来2张证件照,元素名称一样
body.add("idImgList", new FileSystemResource(".\\src\\main\\resources\\2.jpg"));
body.add("idImgList", new FileSystemResource(".\\src\\main\\resources\\3.jpg"));
// 3、头
HttpHeaders headers = new HttpHeaders();
headers.add("header1", "v1");
headers.add("header2", "v2");
// 4、请求实体
RequestEntity<MultiValueMap<String, Object>> requestEntity = new RequestEntity<>(body, headers, HttpMethod.POST, URI.create(url));
// 5、发送请求(请求实体,返回值需要转换的类型)
ResponseEntity<Map<String, String>> responseEntity = restTemplate.exchange(requestEntity, new ParameterizedTypeReference<Map<String, String>>() {
});
Map<String, String> result = responseEntity.getBody();
// {name=路人, headImg=1.jpg, idImgList=[2.jpg, 3.jpg]}
System.out.println(result);
}
接口:
/**
* body中json格式的数据,返回值非泛型
*
* @param bookDto
* @return
*/
@PostMapping("/test/form4")
@ResponseBody
public BookDto form4(@RequestBody BookDto bookDto) {
return bookDto;
}
RestTemplate 调用接口:
@Test
public void test15() {
RestTemplate restTemplate = new RestTemplate();
String url = "http://localhost:8080/test/form4";
BookDto body = new BookDto(1, "SpringMVC系列");
BookDto result = restTemplate.postForObject(url, body, BookDto.class);
// BookDto{id=1, name='SpringMVC系列'}
System.out.println(result);
}
接口:
/**
* body中json格式的数据,返回值为泛型
*
* @param bookDtoList
* @return
*/
@PostMapping("/test/form5")
@ResponseBody
public List<BookDto> form5(@RequestBody List<BookDto> bookDtoList) {
return bookDtoList;
}
用 RestTemplate 调用这个接口,代码如下:
@Test
public void test16() {
RestTemplate restTemplate = new RestTemplate();
String url = "http://localhost:8080/test/form5";
// 1、请求体,发送的时候会被转换为json格式数据
List<BookDto> body = Arrays.asList(new BookDto(1, "SpringMVC系列"), new BookDto(2, "MySQL系列"));
// 2、头
HttpHeaders headers = new HttpHeaders();
headers.add("header1", "v1");
headers.add("header2", "v2");
// 3、请求实体
RequestEntity requestEntity = new RequestEntity(body, headers, HttpMethod.POST, URI.create(url));
// 4、发送请求(请求实体,返回值需要转换的类型)
ResponseEntity<List<BookDto>> responseEntity = restTemplate.exchange(requestEntity, new ParameterizedTypeReference<List<BookDto>>() {
});
// 5、获取结果
List<BookDto> result = responseEntity.getBody();
// [BookDto{id=1, name='SpringMVC系列'}, BookDto{id=2, name='MySQL系列'}]
System.out.println(result);
}
上面 2 个 json 案例 body 都是 java 对象,RestTemplate 默认自动配上 Content-Type=application/json
但是如果 body 的值是 json 格式字符串的时候,调用的时候需要在头中明确指定 Content-
Type=application/json,写法如下:
@Test
public void test17() {
RestTemplate restTemplate = new RestTemplate();
String url = "http://localhost:8080/test/form5";
// 1、请求体为一个json格式的字符串
String body = "[{\"id\":1,\"name\":\"SpringMVC系列\"},{\"id\":2,\"name\":\"MySQL系列\"}]";
/**
* 2、若请求体为json字符串的时候,需要在头中设置Content-Type=application/json
* 若body是普通的java类的时候,无需指定这个,RestTemplate默认自动配上Content-Type=application/json
*/
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
// 3、请求实体(body、头、请求方式、uri)
RequestEntity requestEntity = new RequestEntity(body, headers, HttpMethod.POST, URI.create(url));
// 4、发送请求(请求实体,返回值需要转换的类型)
ResponseEntity<List<BookDto>> responseEntity = restTemplate.exchange(requestEntity, new ParameterizedTypeReference<List<BookDto>>() {
});
// 5、获取结果
List<BookDto> result = responseEntity.getBody();
// [BookDto{id=1, name='SpringMVC系列'}, BookDto{id=2, name='MySQL系列'}]
System.out.println(result);
}
public void delete(String url, Object... uriVariables);
public void delete(String url, Map<String, ?> uriVariables);
public void delete(URI url);
PUT 请求和 POST 请求类似,将类型改为 PUT 就可以了。
OPTIONS 请求用来探测接口支持哪些 http 方法。
public Set<HttpMethod> optionsForAllow(String url, Object... uriVariables);
public Set<HttpMethod> optionsForAllow(String url, Map<String, ?> uriVariables);
public Set<HttpMethod> optionsForAllow(URI url);
RestTemplate 内部默认用的是 jdk 自带的 HttpURLConnection 发送请求的,性能上面并不是太突出。
可以将其替换为 httpclient 或者 okhttp。
先来看下如何替换为 HttpClient。
引入 maven 配置:
<dependency>
<groupId>org.apache.httpcomponentsgroupId>
<artifactId>httpclientartifactId>
<version>4.5.7version>
dependency>
创建 RestTemplate 时指定 HttpClient 配置,代码如下:
public HttpClient httpClient() {
HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
try {
// 设置信任ssl访问
SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, (arg0, arg1) -> true).build();
httpClientBuilder.setSSLContext(sslContext);
HostnameVerifier hostnameVerifier = NoopHostnameVerifier.INSTANCE;
SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslContext, hostnameVerifier);
Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
// 注册http和https请求
.register("http", PlainConnectionSocketFactory.getSocketFactory()).register("https", sslConnectionSocketFactory).build();
// 使用Httpclient连接池的方式配置(推荐),同时支持netty,okHttp以及其他http框架
PoolingHttpClientConnectionManager poolingHttpClientConnectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
// 最大连接数
poolingHttpClientConnectionManager.setMaxTotal(1000);
// 同路由并发数
poolingHttpClientConnectionManager.setDefaultMaxPerRoute(100);
// 配置连接池
httpClientBuilder.setConnectionManager(poolingHttpClientConnectionManager);
// 重试次数
httpClientBuilder.setRetryHandler(new DefaultHttpRequestRetryHandler(0, true));
// 设置默认请求头
List<Header> headers = new ArrayList<>();
httpClientBuilder.setDefaultHeaders(headers);
return httpClientBuilder.build();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public ClientHttpRequestFactory clientHttpRequestFactory() {
HttpComponentsClientHttpRequestFactory clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory(httpClient());
// 连接超时(毫秒),这里设置10秒
clientHttpRequestFactory.setConnectTimeout(10 * 1000);
// 数据读取超时时间(毫秒),这里设置60秒
clientHttpRequestFactory.setReadTimeout(60 * 1000);
// 从连接池获取请求连接的超时时间(毫秒),不宜过长,必须设置,比如连接不够用时,。时间过长将是灾难性的
clientHttpRequestFactory.setConnectionRequestTimeout(10 * 1000);
return clientHttpRequestFactory;
}
public RestTemplate restTemplate() {
//创建RestTemplate的时候,指定ClientHttpRequestFactory
return new RestTemplate(this.clientHttpRequestFactory());
}
@Test
public void test18() {
RestTemplate restTemplate = this.restTemplate();
String url = "http://localhost:8080/test/get";
//getForObject方法,获取响应体,将其转换为第二个参数指定的类型
BookDto bookDto = restTemplate.getForObject(url, BookDto.class);
// BookDto{id=1, name='SpringMVC系列'}
System.out.println(bookDto);
}
引入 maven 配置:
<dependency>
<groupId>com.squareup.okhttp3groupId>
<artifactId>okhttpartifactId>
<version>4.3.1version>
dependency>
创建 RestTemplate:
new RestTemplate(new OkHttp3ClientHttpRequestFactory());