一文吃透接口調(diào)用神器RestTemplate
文末可以領(lǐng)取所有系列高清 pdf。
大家好,我是路人,這是 SpringMVC 系列第 21 篇。
本文介紹 Spring web 中特別牛逼的一個類 RestTemplate。
目錄
1、RestTemplate 概述
2、案例代碼
2.1、git 地址
2.2、關(guān)鍵代碼位置
2.3、如何運(yùn)行測試用例?
3、發(fā)送 Get 請求
3.1、普通請求
3.2、url 中含有動態(tài)參數(shù)
3.3、接口返回值為泛型
3.4、下載小文件
3.5、下載大文件
3.6、傳遞頭
3.7、綜合案例:含頭、url 動態(tài)參數(shù)
4、POST 請求
4.1、post 請求常見的 3 種類型
4.2、普通表單請求
4.3、上傳本地文件
4.4、通過流或字節(jié)數(shù)組的方式上傳文件
4.5、復(fù)雜表單:多個普通元素+多文件上傳
4.6、發(fā)送 json 格式數(shù)據(jù):傳遞 java 對象
4.7、發(fā)送 json 格式數(shù)據(jù):傳遞 java 對象,返回值為泛型
4.8、發(fā)送 json 字符串格式數(shù)據(jù)
5、DELETE、PUT、OPTION 請求
5.1、DELETE 請求
5.2、PUT 請求
5.3、OPTIONS 請求
6、集成 HttpClient
7、集成 okhttp
8、總結(jié)
9、SpringMVC 系列目錄
10、更多好文章
11、【路人甲 Java】所有系列高清 PDF
1、RestTemplate 概述
發(fā)送 http 請求,估計(jì)很多人用過 httpclient 和 okhttp,確實(shí)挺好用的,而 Spring web 中的 RestTemplate 和這倆的功能類似,也是用來發(fā)送 http 請求的,不過用法上面比前面的 2 位要容易很多。
spring 框架提供的 RestTemplate 類可用于在應(yīng)用中調(diào)用 rest 服務(wù),它簡化了與 http 服務(wù)的通信方式,統(tǒng)一了 RESTful 的標(biāo)準(zhǔn),封裝了 http 鏈接, 我們只需要傳入 url 及返回值類型即可。相較于之前常用的 HttpClient,RestTemplate 是一種更優(yōu)雅的調(diào)用 RESTful 服務(wù)的方式。
在 Spring 應(yīng)用程序中訪問第三方 REST 服務(wù)與使用 Spring RestTemplate 類有關(guān)。RestTemplate 類的設(shè)計(jì)原則與許多其他 Spring 模板類(例如 JdbcTemplate、JmsTemplate)相同,為執(zhí)行復(fù)雜任務(wù)提供了一種具有默認(rèn)行為的簡化方法。
RestTemplate 默認(rèn)依賴 JDK 提供 http 連接的能力(HttpURLConnection),如果有需要的話也可以通過 setRequestFactory 方法替換為例如 Apache HttpComponents、Netty 或 OkHttp 等其它 HTTP library。
考慮到 RestTemplate 類是為調(diào)用 REST 服務(wù)而設(shè)計(jì)的,因此它的主要方法與 REST 的基礎(chǔ)緊密相連就不足為奇了,后者是 HTTP 協(xié)議的方法:HEAD、GET、POST、PUT、DELETE 和 OPTIONS。例如,RestTemplate 類具有 headForHeaders()、getForObject()、postForObject()、put()和 delete()等方法。
下面給大家上案例,案例是重點(diǎn),通過案例,把我知道的用法都給盤出來。
2、案例代碼
2.1、git 地址
https://gitee.com/javacode2018/springmvc-series

2.2、關(guān)鍵代碼位置
文中的所有 controller 代碼,在RestTemplateTestController類中。
所有@Test 用例的代碼,在RestTemplateTest。

2.3、如何運(yùn)行測試用例?
拉取項(xiàng)目 將 chat16-RestTemplate 模塊發(fā)布到 tomcat9 中 運(yùn)行 RestTemplateTest 中對應(yīng)的用例即可
下面咱們來看 RestTemplate 常見的用法匯總。
3、發(fā)送 Get 請求
3.1、普通請求
接口代碼
@GetMapping("/test/get")
@ResponseBody
public?BookDto?get()?{
????return?new?BookDto(1,?"SpringMVC系列");
}
使用 RestTemplate 調(diào)用上面這個接口,通常有 2 種寫法,如下
@Test
public?void?test1()?{
????RestTemplate?restTemplate?=?new?RestTemplate();
????String?url?=?"http://localhost:8080/chat16/test/get";
????//getForObject方法,獲取響應(yīng)體,將其轉(zhuǎn)換為第二個參數(shù)指定的類型
????BookDto?bookDto?=?restTemplate.getForObject(url,?BookDto.class);
????System.out.println(bookDto);
}
@Test
public?void?test2()?{
????RestTemplate?restTemplate?=?new?RestTemplate();
????String?url?=?"http://localhost:8080/chat16/test/get";
????//getForEntity方法,返回值為ResponseEntity類型
????//?ResponseEntity中包含了響應(yīng)結(jié)果中的所有信息,比如頭、狀態(tài)、body
????ResponseEntity?responseEntity?=?restTemplate.getForEntity(url,?BookDto.class);
????//狀態(tài)碼
????System.out.println(responseEntity.getStatusCode());
????//獲取頭
????System.out.println("頭:"?+?responseEntity.getHeaders());
????//獲取body
????BookDto?bookDto?=?responseEntity.getBody();
????System.out.println(bookDto);
}
test1 輸出
BookDto{id=1,?name='SpringMVC系列'}
test2 輸出
200?OK
頭:[Content-Type:"application/json;charset=UTF-8",?Transfer-Encoding:"chunked",?Date:"Sat,?02?Oct?2021?07:05:15?GMT",?Keep-Alive:"timeout=20",?Connection:"keep-alive"]
BookDto{id=1,?name='SpringMVC系列'}
3.2、url 中含有動態(tài)參數(shù)
接口代碼
@GetMapping("/test/get/{id}/{name}")
@ResponseBody
public?BookDto?get(@PathVariable("id")?Integer?id,?@PathVariable("name")?String?name)?{
????return?new?BookDto(id,?name);
}
使用 RestTemplate 調(diào)用上面這個接口,通常有 2 種寫法,如下
@Test
public?void?test3()?{
????RestTemplate?restTemplate?=?new?RestTemplate();
????//url中有動態(tài)參數(shù)
????String?url?=?"http://localhost:8080/chat16/test/get/{id}/{name}";
????Map?uriVariables?=?new?HashMap<>();
????uriVariables.put("id",?"1");
????uriVariables.put("name",?"SpringMVC系列");
????//使用getForObject或者getForEntity方法
????BookDto?bookDto?=?restTemplate.getForObject(url,?BookDto.class,?uriVariables);
????System.out.println(bookDto);
}
@Test
public?void?test4()?{
????RestTemplate?restTemplate?=?new?RestTemplate();
????//url中有動態(tài)參數(shù)
????String?url?=?"http://localhost:8080/chat16/test/get/{id}/{name}";
????Map?uriVariables?=?new?HashMap<>();
????uriVariables.put("id",?"1");
????uriVariables.put("name",?"SpringMVC系列");
????//getForEntity方法
????ResponseEntity?responseEntity?=?restTemplate.getForEntity(url,?BookDto.class,?uriVariables);
????BookDto?bookDto?=?responseEntity.getBody();
????System.out.println(bookDto);
}
test3 輸出
BookDto{id=1,?name='SpringMVC系列'}
test4 輸出
BookDto{id=1,?name='SpringMVC系列'}
3.3、接口返回值為泛型
接口代碼
@GetMapping("/test/getList")
@ResponseBody
public?List?getList()? {
????return?Arrays.asList(
????????????new?BookDto(1,?"Spring高手系列"),
????????????new?BookDto(2,?"SpringMVC系列")
????);
}
當(dāng)接口的返回值為泛型的時候,這種情況比較特殊,使用 RestTemplate 調(diào)用上面這個接口,代碼如下,需要用到restTemplate.exchange的方法,這個方法中有個參數(shù)是ParameterizedTypeReference類型,通過這個參數(shù)類指定泛型類型
@Test
public?void?test5()?{
????RestTemplate?restTemplate?=?new?RestTemplate();
????//返回值為泛型
????String?url?=?"http://localhost:8080/chat16/test/getList";
????//若返回結(jié)果是泛型類型的,需要使用到exchange方法,
????//這個方法中有個參數(shù)是ParameterizedTypeReference類型,通過這個參數(shù)類指定泛型類型
????ResponseEntity>?responseEntity?=
????????????restTemplate.exchange(url,
????????????????????HttpMethod.GET,
????????????????????null,
????????????????????new?ParameterizedTypeReference>()?{
????????????????????});
????List?bookDtoList?=?responseEntity.getBody();
????System.out.println(bookDtoList);
}
輸出
[BookDto{id=1,?name='Spring高手系列'},?BookDto{id=2,?name='SpringMVC系列'}]
3.4、下載小文件
接口代碼如下,這個接口會下載服務(wù)器端的 1.txt 文件。
/**
?*?下載文件
?*
?*?@return
?*/
@GetMapping("/test/downFile")
@ResponseBody
public?HttpEntity?downFile()? {
????//將文件流封裝為InputStreamResource對象
????InputStream?inputStream?=?this.getClass().getResourceAsStream("/1.txt");
????InputStreamResource?inputStreamResource?=?new?InputStreamResource(inputStream);
????//設(shè)置header
????MultiValueMap?headers?=?new?HttpHeaders();
????headers.add(HttpHeaders.CONTENT_DISPOSITION,?"attachment;filename=1.txt");
????HttpEntity?httpEntity?=?new?HttpEntity<>(inputStreamResource);
????return?httpEntity;
}
使用 RestTemplate 調(diào)用這個接口,代碼如下,目前這個文件的內(nèi)容比較少,可以直接得到一個數(shù)組。
@Test
public?void?test6()?{
????RestTemplate?restTemplate?=?new?RestTemplate();
????String?url?=?"http://localhost:8080/chat16/test/downFile";
????//文件比較小的情況,直接返回字節(jié)數(shù)組
????ResponseEntity<byte[]>?responseEntity?=?restTemplate.getForEntity(url,?byte[].class);
????//獲取文件的內(nèi)容
????byte[]?body?=?responseEntity.getBody();
????String?content?=?new?String(body);
????System.out.println(content);
}
注意:如果文件大的時候,這種方式就有問題了,會導(dǎo)致 oom,要用下面的方式了。
3.5、下載大文件
接口代碼,繼續(xù)使用上面下載 1.txt 的代碼
/**
?*?下載文件
?*
?*?@return
?*/
@GetMapping("/test/downFile")
@ResponseBody
public?HttpEntity?downFile()? {
????//將文件流封裝為InputStreamResource對象
????InputStream?inputStream?=?this.getClass().getResourceAsStream("/1.txt");
????InputStreamResource?inputStreamResource?=?new?InputStreamResource(inputStream);
????//設(shè)置header
????MultiValueMap?headers?=?new?HttpHeaders();
????headers.add(HttpHeaders.CONTENT_DISPOSITION,?"attachment;filename=1.txt");
????HttpEntity?httpEntity?=?new?HttpEntity<>(inputStreamResource);
????return?httpEntity;
}
此時使用 RestTemplate 調(diào)用這個接口,代碼如下
文件比較大的時候,比如好幾個 G,就不能返回字節(jié)數(shù)組了,會把內(nèi)存撐爆,導(dǎo)致 OOM,需要使用 execute 方法了,這個方法中有個 ResponseExtractor 類型的參數(shù),restTemplate 拿到結(jié)果之后,會回調(diào){@link ResponseExtractor#extractData}這個方法,在這個方法中可以拿到響應(yīng)流,然后進(jìn)行處理,這個過程就是變讀邊處理,不會導(dǎo)致內(nèi)存溢出
@Test
public?void?test7()?{
????RestTemplate?restTemplate?=?new?RestTemplate();
????String?url?=?"http://localhost:8080/chat16/test/downFile";
????/**
?????*?文件比較大的時候,比如好幾個G,就不能返回字節(jié)數(shù)組了,會把內(nèi)存撐爆,導(dǎo)致OOM
?????*?需要這么玩:
?????*?需要使用execute方法了,這個方法中有個ResponseExtractor類型的參數(shù),
?????*?restTemplate拿到結(jié)果之后,會回調(diào){@link?ResponseExtractor#extractData}這個方法,
?????*?在這個方法中可以拿到響應(yīng)流,然后進(jìn)行處理,這個過程就是變讀邊處理,不會導(dǎo)致內(nèi)存溢出
?????*/
????String?result?=?restTemplate.execute(url,
????????????HttpMethod.GET,
????????????null,
????????????new?ResponseExtractor()?{
????????????????@Override
????????????????public?String?extractData(ClientHttpResponse?response)?throws?IOException?{
????????????????????System.out.println("狀態(tài):"+response.getStatusCode());
????????????????????System.out.println("頭:"+response.getHeaders());
????????????????????//獲取響應(yīng)體流
????????????????????InputStream?body?=?response.getBody();
????????????????????//處理響應(yīng)體流
????????????????????String?content?=?IOUtils.toString(body,?"UTF-8");
????????????????????return?content;
????????????????}
????????????},?new?HashMap<>());
????System.out.println(result);
}
3.6、傳遞頭
接口代碼
@GetMapping("/test/header")
@ResponseBody
public?Map>?header(HttpServletRequest?request)?{
????Map>?header?=?new?LinkedHashMap<>();
????Enumeration?headerNames?=?request.getHeaderNames();
????while?(headerNames.hasMoreElements())?{
????????String?name?=?headerNames.nextElement();
????????Enumeration?values?=?request.getHeaders(name);
????????List?list?=?new?ArrayList<>();
????????while?(values.hasMoreElements())?{
????????????list.add(values.nextElement());
????????}
????????header.put(name,?list);
????}
????return?header;
}
使用 RestTemplate 調(diào)用接口,請求頭中傳遞數(shù)據(jù),代碼如下,注意代碼①和②,這兩處是關(guān)鍵,用到了HttpHeaders和RequestEntity
請求頭放在 HttpHeaders 對象中 RequestEntity:請求實(shí)體,請求的所有信息都可以放在 RequestEntity 中,比如 body 部分、頭、請求方式、url 等信息
@Test
public?void?test8()?{
????RestTemplate?restTemplate?=?new?RestTemplate();
????String?url?=?"http://localhost:8080/chat16/test/header";
????//①:請求頭放在HttpHeaders對象中
????MultiValueMap?headers?=?new?HttpHeaders();
????headers.add("header-1",?"V1");
????headers.add("header-2",?"Spring");
????headers.add("header-2",?"SpringBoot");
????//②:RequestEntity:請求實(shí)體,請求的所有信息都可以放在RequestEntity中,比如body部分、頭、請求方式、url等信息
????RequestEntity?requestEntity?=?new?RequestEntity(
????????????null,?//body部分?jǐn)?shù)據(jù)
????????????headers,?//頭
????????????HttpMethod.GET,//請求方法
????????????URI.create(url)?//地址
????);
????ResponseEntity 輸出
{accept=[application/json,?application/*+json],?header-1=[V1],?header-2=[Spring,?SpringBoot],?user-agent=[Java/1.8.0_121],?host=[localhost:8080],?connection=[keep-alive]}
3.7、綜合案例:含頭、url 動態(tài)參數(shù)
接口
@GetMapping("/test/getAll/{path1}/{path2}")
@ResponseBody
public?Map?getAll(@PathVariable("path1")?String?path1,
??????????????????????????????????@PathVariable("path2")?String?path2,
??????????????????????????????????HttpServletRequest?request)? {
????Map?result?=?new?LinkedHashMap<>();
????result.put("path1",?path1);
????result.put("path2",?path2);
????//頭
????Map>?header?=?new?LinkedHashMap<>();
????Enumeration?headerNames?=?request.getHeaderNames();
????while?(headerNames.hasMoreElements())?{
????????String?name?=?headerNames.nextElement();
????????Enumeration?values?=?request.getHeaders(name);
????????List?list?=?new?ArrayList<>();
????????while?(values.hasMoreElements())?{
????????????list.add(values.nextElement());
????????}
????????header.put(name,?list);
????}
????result.put("header",?header);
????return?result;
}
如下,使用 RestTemplate 調(diào)用接口,GET 方式、傳遞 header、path 中動態(tài)參數(shù)。
@Test
public?void?test9()?{
????RestTemplate?restTemplate?=?new?RestTemplate();
????String?url?=?"http://localhost:8080/chat16/test/getAll/{path1}/{path2}";
????//①:請求頭
????MultiValueMap?headers?=?new?HttpHeaders();
????headers.add("header-1",?"V1");
????headers.add("header-2",?"Spring");
????headers.add("header-2",?"SpringBoot");
????//②:url中的2個參數(shù)
????Map?uriVariables?=?new?HashMap<>();
????uriVariables.put("path1",?"v1");
????uriVariables.put("path2",?"v2");
????//③:HttpEntity:HTTP實(shí)體,內(nèi)部包含了請求頭和請求體
????HttpEntity?requestEntity?=?new?HttpEntity(
????????null,//body部分,get請求沒有body,所以為null
????????headers?//頭
????);
????//④:使用exchange發(fā)送請求
????ResponseEntity>?responseEntity?=?restTemplate.exchange(
????????url,?//url
????????HttpMethod.GET,?//請求方式
????????requestEntity,?//請求實(shí)體(頭、body)
????????new?ParameterizedTypeReference>()?{
????????},//返回的結(jié)果類型
????????uriVariables?//url中的占位符對應(yīng)的值
????);
????Map?result?=?responseEntity.getBody();
????System.out.println(result);
}
輸出
{path1=v1,?path2=v2,?header={accept=[application/json,?application/*+json],?header-1=[V1],?header-2=[Spring,?SpringBoot],?user-agent=[Java/1.8.0_121],?host=[localhost:8080],?connection=[keep-alive]}}
4、POST 請求
4.1、post 請求常見的 3 種類型
http 請求頭中的 Content-Type 用來指定請求的類型,常見的有 3 種
| Content-Type | 說明 |
|---|---|
| application/x-www-form-urlencoded | 頁面中普通的 form 表單提交時就是這種類型,表單中的元素會按照名稱和值拼接好,然后之間用&連接,格式如:p1=v1&p2=v2&p3=v3 然后通過 urlencoded 編碼之后丟在 body 中發(fā)送 |
| multipart/form-data | 頁面中表單上傳文件的時候,用到的就是這種格式 |
| application/json | 將發(fā)送的數(shù)據(jù)轉(zhuǎn)換為 json 格式,丟在 http 請求的 body 中發(fā)送,后端接口通常用@RequestBody 配合對象來接收。 |
下面看則種方式的案例。
4.2、普通表單請求
普通表單默認(rèn)為 application/x-www-form-urlencoded 類型的請求。
接口代碼
@PostMapping("/test/form1")
@ResponseBody
public?BookDto?form1(BookDto?bookDto)?{
????return?bookDto;
}
使用 RestTemplate 調(diào)用接口
@Test
public?void?test10()?{
????RestTemplate?restTemplate?=?new?RestTemplate();
????String?url?=?"http://localhost:8080/chat16/test/form1";
????//①:表單信息,需要放在MultiValueMap中,MultiValueMap相當(dāng)于Map>
????MultiValueMap?body?=?new?LinkedMultiValueMap<>();
????//調(diào)用add方法填充表單數(shù)據(jù)(表單名稱:值)
????body.add("id","1");
????body.add("name","SpringMVC系列");
????//②:發(fā)送請求(url,請求體,返回值需要轉(zhuǎn)換的類型)
????BookDto?result?=?restTemplate.postForObject(url,?body,?BookDto.class);
????System.out.println(result);
}
如果想攜帶頭信息,代碼如下
@Test
public?void?test11()?{
????RestTemplate?restTemplate?=?new?RestTemplate();
????String?url?=?"http://localhost:8080/chat16/test/form1";
????//①:表單信息,需要放在MultiValueMap中,MultiValueMap相當(dāng)于Map>
????MultiValueMap?body?=?new?LinkedMultiValueMap<>();
????//調(diào)用add方法放入表單元素(表單名稱:值)
????body.add("id","1");
????body.add("name","SpringMVC系列");
????//②:請求頭
????HttpHeaders?headers?=?new?HttpHeaders();
????//調(diào)用set方法放入請求頭
????headers.set(HttpHeaders.CONTENT_TYPE,?MediaType.APPLICATION_FORM_URLENCODED_VALUE);
????//③:請求實(shí)體:包含了請求體和請求頭
????HttpEntity>?httpEntity?=?new?HttpEntity<>(body,?headers);
????//④:發(fā)送請求(url,請求實(shí)體,返回值需要轉(zhuǎn)換的類型)
????BookDto?result?=?restTemplate.postForObject(url,?httpEntity,?BookDto.class);
????System.out.println(result);
}
4.3、上傳本地文件
上傳文件 Content-Type 為 multipart/form-data 類型。
接口如下,上傳上傳單個文件,返回值為一個 Map 類型,是泛型類型
@PostMapping(value?=?"/test/form2")
@ResponseBody
public?Map?form2(@RequestParam("file1")?MultipartFile?file1)? {
????Map?fileMetadata?=?new?LinkedHashMap<>();
????fileMetadata.put("文件名",?file1.getOriginalFilename());
????fileMetadata.put("文件類型",?file1.getContentType());
????fileMetadata.put("文件大小(byte)",?String.valueOf(file1.getSize()));
????return?fileMetadata;
}
使用 RestTemplate 調(diào)用接口,主要下面代碼②上傳的文件需要包裝為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/chat16/test/form2";
????//①:表單信息,需要放在MultiValueMap中,MultiValueMap相當(dāng)于Map>
????MultiValueMap?body?=?new?LinkedMultiValueMap<>();
????//調(diào)用add方法放入表單元素(表單名稱:值)
????//②:文件對應(yīng)的類型,需要是org.springframework.core.io.Resource類型的,常見的有[FileSystemResource、InputStreamResource、ByteArrayResource]
????body.add("file1",?new?FileSystemResource(".\\src\\main\\java\\com\\javacode2018\\springmvc\\chat16\\dto\\UserDto.java"));
????//③:頭
????HttpHeaders?headers?=?new?HttpHeaders();
????headers.add("header1",?"v1");
????headers.add("header2",?"v2");
????//④:請求實(shí)體
????RequestEntity>?requestEntity?=?new?RequestEntity<>(body,?headers,?HttpMethod.POST,?URI.create(url));
????//⑤:發(fā)送請求(請求實(shí)體,返回值需要轉(zhuǎn)換的類型)
????ResponseEntity>?responseEntity?=?restTemplate.exchange(
????????requestEntity,
????????new?ParameterizedTypeReference>()?{
????????});
????Map?result?=?responseEntity.getBody();
????System.out.println(result);
}
4.4、通過流或字節(jié)數(shù)組的方式上傳文件
有時候,上傳的文件是通過流的方式或者字節(jié)數(shù)組的方式,那么就需要用到 InputStreamResource、ByteArrayResource 這倆了。
**注意:**使用這倆的時候,需要重寫 2 個方法,否則會上傳失敗
getFilename:文件名稱 contentLength:長度
@Test
public?void?test13()?{
????RestTemplate?restTemplate?=?new?RestTemplate();
????String?url?=?"http://localhost:8080/chat16/test/form2";
????//①:表單信息,需要放在MultiValueMap中,MultiValueMap相當(dāng)于Map>
????MultiValueMap?body?=?new?LinkedMultiValueMap<>();
????/**
?????*?②:通過流的方式上傳文件,流的方式需要用到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);
????//③:頭
????HttpHeaders?headers?=?new?HttpHeaders();
????headers.add("header1",?"v1");
????headers.add("header2",?"v2");
????//④:請求實(shí)體
????RequestEntity>?requestEntity?=?new?RequestEntity<>(body,?headers,?HttpMethod.POST,?URI.create(url));
????//⑤:發(fā)送請求(請求實(shí)體,返回值需要轉(zhuǎn)換的類型)
????ResponseEntity>?responseEntity?=?restTemplate.exchange(
????????????requestEntity,
????????????new?ParameterizedTypeReference>()?{
????????????});
????Map?result?=?responseEntity.getBody();
????System.out.println(result);
}
4.5、復(fù)雜表單:多個普通元素+多文件上傳
接口
/**
?*?復(fù)雜的表單:包含了普通元素、多文件
?*
?*?@param?userDto
?*?@return
?*/
@PostMapping("/test/form3")
@ResponseBody
public?Map?form3(UserDto?userDto)? {
????Map?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:包含了多個元素(姓名、頭像、多張證件照),這種可以模擬復(fù)雜的表單
public?class?UserDto?{
????//姓名
????private?String?name;
????//頭像
????private?MultipartFile?headImg;
????//多張證件照
????private?List?idImgList;
????//get?set?省略了...
}
用 RestTemplate 調(diào)用這個接口,代碼如下
@Test
public?void?test14()?{
????RestTemplate?restTemplate?=?new?RestTemplate();
????String?url?=?"http://localhost:8080/chat16/test/form3";
????//①:表單信息,需要放在MultiValueMap中,MultiValueMap相當(dāng)于Map>
????MultiValueMap?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"));
????//③:頭
????HttpHeaders?headers?=?new?HttpHeaders();
????headers.add("header1",?"v1");
????headers.add("header2",?"v2");
????//④:請求實(shí)體
????RequestEntity>?requestEntity?=?new?RequestEntity<>(body,?headers,?HttpMethod.POST,?URI.create(url));
????//⑤:發(fā)送請求(請求實(shí)體,返回值需要轉(zhuǎn)換的類型)
????ResponseEntity>?responseEntity?=?restTemplate.exchange(
????????????requestEntity,
????????????new?ParameterizedTypeReference>()?{
????????????});
????Map?result?=?responseEntity.getBody();
????System.out.println(result);
}
輸出
{name=路人,?headImg=1.jpg,?idImgList=[2.jpg,?3.jpg]}
4.6、發(fā)送 json 格式數(shù)據(jù):傳遞 java 對象
接口
/**
?*?body中json格式的數(shù)據(jù),返回值非泛型
?*
?*?@param?bookDto
?*?@return
?*/
@PostMapping("/test/form4")
@ResponseBody
public?BookDto?form4(@RequestBody?BookDto?bookDto)?{
????return?bookDto;
}
RestTemplate 調(diào)用接口
@Test
public?void?test15()?{
????RestTemplate?restTemplate?=?new?RestTemplate();
????String?url?=?"http://localhost:8080/chat16/test/form4";
????BookDto?body?=?new?BookDto(1,?"SpringMVC系列");
????BookDto?result?=?restTemplate.postForObject(url,?body,?BookDto.class);
????System.out.println(result);
}
輸出
BookDto{id=1,?name='SpringMVC系列'}
4.7、發(fā)送 json 格式數(shù)據(jù):傳遞 java 對象,返回值為泛型
接口
/**
?*?body中json格式的數(shù)據(jù),返回值為泛型
?*
?*?@param?bookDtoList
?*?@return
?*/
@PostMapping("/test/form5")
@ResponseBody
public?List?form5(@RequestBody?List?bookDtoList) ? {
????return?bookDtoList;
}
用 RestTemplate 調(diào)用這個接口,代碼如下
@Test
public?void?test16()?{
????RestTemplate?restTemplate?=?new?RestTemplate();
????String?url?=?"http://localhost:8080/chat16/test/form5";
????//①:請求體,發(fā)送的時候會被轉(zhuǎn)換為json格式數(shù)據(jù)
????List?body?=?Arrays.asList(
????????????new?BookDto(1,?"SpringMVC系列"),
????????????new?BookDto(2,?"MySQL系列"));
????//②:頭
????HttpHeaders?headers?=?new?HttpHeaders();
????headers.add("header1",?"v1");
????headers.add("header2",?"v2");
????//③:請求實(shí)體
????RequestEntity?requestEntity?=?new?RequestEntity(body,?headers,?HttpMethod.POST,?URI.create(url));
????//④:發(fā)送請求(請求實(shí)體,返回值需要轉(zhuǎn)換的類型)
????ResponseEntity>?responseEntity?=?restTemplate.exchange(
????????????requestEntity,
????????????new?ParameterizedTypeReference>()?{
????????????});
????//⑤:獲取結(jié)果
????List?result?=?responseEntity.getBody();
????System.out.println(result);
}
輸出
[BookDto{id=1,?name='SpringMVC系列'},?BookDto{id=2,?name='MySQL系列'}]
4.8、發(fā)送 json 字符串格式數(shù)據(jù)
上面 2 個 json 案例 body 都是 java 對象,RestTemplate 默認(rèn)自動配上 Content-Type=application/json
但是如果 body 的值是 json 格式字符串的時候,調(diào)用的時候需要在頭中明確指定 Content-Type=application/json,寫法如下:
@Test
public?void?test17()?{
????RestTemplate?restTemplate?=?new?RestTemplate();
????String?url?=?"http://localhost:8080/chat16/test/form5";
????//①:請求體為一個json格式的字符串
????String?body?=?"[{\"id\":1,\"name\":\"SpringMVC系列\(zhòng)"},{\"id\":2,\"name\":\"MySQL系列\(zhòng)"}]";
????/**
?????*?②:若請求體為json字符串的時候,需要在頭中設(shè)置Content-Type=application/json;
?????*?若body是普通的java類的時候,無需指定這個,RestTemplate默認(rèn)自動配上Content-Type=application/json
?????*/
????HttpHeaders?headers?=?new?HttpHeaders();
????headers.setContentType(MediaType.APPLICATION_JSON);
????//③:請求實(shí)體(body,頭、請求方式,uri)
????RequestEntity?requestEntity?=?new?RequestEntity(body,?headers,?HttpMethod.POST,?URI.create(url));
????//④:發(fā)送請求(請求實(shí)體,返回值需要轉(zhuǎn)換的類型)
????ResponseEntity>?responseEntity?=?restTemplate.exchange(
????????????requestEntity,
????????????new?ParameterizedTypeReference>()?{
????????????});
????//⑤:獲取結(jié)果
????List?result?=?responseEntity.getBody();
????System.out.println(result);
}
輸出
[BookDto{id=1,?name='SpringMVC系列'},?BookDto{id=2,?name='MySQL系列'}]
5、DELETE、PUT、OPTION 請求
5.1、DELETE 請求
public?void?delete(String?url,?Object...?uriVariables);
public?void?delete(String?url,?Map?uriVariables) ;
public?void?delete(URI?url);
5.2、PUT 請求
PUT 請求和 POST 請求類似,將類型改為 PUT 就可以了。
5.3、OPTIONS 請求
OPTIONS 請求用來探測接口支持哪些 http 方法
public?Set?optionsForAllow(String?url,?Object...?uriVariables) ;
public?Set?optionsForAllow(String?url,?Map?uriVariables) ;
public?Set?optionsForAllow(URI?url) ;
6、集成 HttpClient
RestTemplate 內(nèi)部默認(rèn)用的是 jdk 自帶的 HttpURLConnection 發(fā)送請求的,性能上面并不是太突出。
可以將其替換為 httpclient 或者 okhttp。
先來看下如何替換為 HttpClient。
引入 maven 配置
<dependency>
????<groupId>org.apache.httpcomponentsgroupId>
????<artifactId>httpclientartifactId>
????<version>4.5.7version>
dependency>
創(chuàng)建 RestTemplate 時指定 HttpClient 配置,代碼如下
public?HttpClient?httpClient()?{
????HttpClientBuilder?httpClientBuilder?=?HttpClientBuilder.create();
????try?{
????????//設(shè)置信任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?socketFactoryRegistry?=?RegistryBuilder.create()
????????????????//?注冊http和https請求
????????????????.register("http",?PlainConnectionSocketFactory.getSocketFactory())
????????????????.register("https",?sslConnectionSocketFactory).build();
????????//使用Httpclient連接池的方式配置(推薦),同時支持netty,okHttp以及其他http框架
????????PoolingHttpClientConnectionManager?poolingHttpClientConnectionManager?=?new?PoolingHttpClientConnectionManager(socketFactoryRegistry);
????????//?最大連接數(shù)
????????poolingHttpClientConnectionManager.setMaxTotal(1000);
????????//?同路由并發(fā)數(shù)
????????poolingHttpClientConnectionManager.setDefaultMaxPerRoute(100);
????????//配置連接池
????????httpClientBuilder.setConnectionManager(poolingHttpClientConnectionManager);
????????//?重試次數(shù)
????????httpClientBuilder.setRetryHandler(new?DefaultHttpRequestRetryHandler(0,?true));
????????//設(shè)置默認(rèn)請求頭
????????List?headers?=?new?ArrayList<>();
????????httpClientBuilder.setDefaultHeaders(headers);
????????return?httpClientBuilder.build();
????}?catch?(Exception?e)?{
????????throw?new?RuntimeException(e);
????}
}
public?ClientHttpRequestFactory?clientHttpRequestFactory()?{
????HttpComponentsClientHttpRequestFactory?clientHttpRequestFactory?=?new?HttpComponentsClientHttpRequestFactory(httpClient());
????//?連接超時(毫秒),這里設(shè)置10秒
????clientHttpRequestFactory.setConnectTimeout(10?*?1000);
????//?數(shù)據(jù)讀取超時時間(毫秒),這里設(shè)置60秒
????clientHttpRequestFactory.setReadTimeout(60?*?1000);
????//?從連接池獲取請求連接的超時時間(毫秒),不宜過長,必須設(shè)置,比如連接不夠用時,時間過長將是災(zāi)難性的
????clientHttpRequestFactory.setConnectionRequestTimeout(10?*?1000);
????return?clientHttpRequestFactory;
}
public?RestTemplate?restTemplate(){
????//創(chuàng)建RestTemplate的時候,指定ClientHttpRequestFactory
????return?new?RestTemplate(this.clientHttpRequestFactory());
}
@Test
public?void?test18()?{
????RestTemplate?restTemplate?=?this.restTemplate();
????String?url?=?"http://localhost:8080/chat16/test/get";
????//getForObject方法,獲取響應(yīng)體,將其轉(zhuǎn)換為第二個參數(shù)指定的類型
????BookDto?bookDto?=?restTemplate.getForObject(url,?BookDto.class);
????System.out.println(bookDto);
}
7、集成 okhttp
引入 maven 配置
<dependency>
????<groupId>com.squareup.okhttp3groupId>
????<artifactId>okhttpartifactId>
????<version>4.3.1version>
dependency>
創(chuàng)建 RestTemplate
new?RestTemplate(new?OkHttp3ClientHttpRequestFactory());
8、總結(jié)
RestTemplate 使用確實(shí)非常容易,建議大家去看一下 RestTemplate 的源碼,debug 跟蹤一下過程,這樣用起來就非常順手了。
《尚硅谷 Java 學(xué)科全套教程(總 207.77GB)》
9、SpringMVC 系列目錄
SpringMVC 系列第 1 篇:helloword SpringMVC 系列第 2 篇:@Controller、@RequestMapping SpringMVC 系列第 3 篇:異常高效的一款接口測試?yán)?/a> SpringMVC 系列第 4 篇:controller 常見的接收參數(shù)的方式 SpringMVC 系列第 5 篇:@RequestBody 大解密,說點(diǎn)你不知道的 SpringMVC 系列第 6 篇:上傳文件的 4 種方式,你都會么? SpringMVC 系列第 7 篇:SpringMVC 返回視圖常見的 5 種方式,你會幾種? SpringMVC 系列第 8 篇:返回 json & 通用返回值設(shè)計(jì) SpringMVC 系列第 9 篇:SpringMVC 返回 null 是什么意思? SpringMVC 系列第 10 篇:異步處理 SpringMVC 系列第 11 篇:集成靜態(tài)資源 SpringMVC 系列第 12 篇:攔截器 SpringMVC 系列第 13 篇:統(tǒng)一異常處理 SpringMVC 系列第 14 篇:實(shí)戰(zhàn)篇:通用返回值 & 異常處理設(shè)計(jì) SpringMVC 系列第 15 篇:全注解的方式 ?&? 原理解析 SpringMVC 系列第 16 篇:通過源碼解析 SpringMVC 處理請求的流程 SpringMVC 系列第 17 篇:源碼解析 SpringMVC 容器的啟動過程 SpringMVC 系列第 18 篇:強(qiáng)大的 RequestBodyAdvice 解密 SpringMVC 系列第 19 篇:強(qiáng)大的 ResponseBodyAdvice 解密 SpringMVC 系列第 20 篇:RestFull 詳解
10、更多好文章
Spring 高手系列(共 56 篇) Java 高并發(fā)系列(共 34 篇) MySql 高手系列(共 27 篇) Maven 高手系列(共 10 篇) Mybatis 系列(共 12 篇) 聊聊 db 和緩存一致性常見的實(shí)現(xiàn)方式 接口冪等性這么重要,它是什么?怎么實(shí)現(xiàn)? 泛型,有點(diǎn)難度,會讓很多人懵逼,那是因?yàn)槟銢]有看這篇文章!
11、【路人甲 Java】所有系列高清 PDF
領(lǐng)取方式,掃碼發(fā)送:yyds
