<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          一文吃透接口調(diào)用神器RestTemplate

          共 25268字,需瀏覽 51分鐘

           ·

          2021-11-07 00:01

          文末可以領(lǐng)取所有系列高清 pdf。

          大家好,我是路人,這是 SpringMVC 系列第 21 篇。

          本文介紹 Spring web 中特別牛逼的一個(gè)類 RestTemplate。

          目錄

          • 1、RestTemplate 概述

          • 2、案例代碼

            • 2.1、git 地址

            • 2.2、關(guān)鍵代碼位置

            • 2.3、如何運(yùn)行測(cè)試用例?

          • 3、發(fā)送 Get 請(qǐng)求

            • 3.1、普通請(qǐng)求

            • 3.2、url 中含有動(dòng)態(tài)參數(shù)

            • 3.3、接口返回值為泛型

            • 3.4、下載小文件

            • 3.5、下載大文件

            • 3.6、傳遞頭

            • 3.7、綜合案例:含頭、url 動(dòng)態(tài)參數(shù)

          • 4、POST 請(qǐng)求

            • 4.1、post 請(qǐng)求常見的 3 種類型

            • 4.2、普通表單請(qǐng)求

            • 4.3、上傳本地文件

            • 4.4、通過流或字節(jié)數(shù)組的方式上傳文件

            • 4.5、復(fù)雜表單:多個(gè)普通元素+多文件上傳

            • 4.6、發(fā)送 json 格式數(shù)據(jù):傳遞 java 對(duì)象

            • 4.7、發(fā)送 json 格式數(shù)據(jù):傳遞 java 對(duì)象,返回值為泛型

            • 4.8、發(fā)送 json 字符串格式數(shù)據(jù)

          • 5、DELETE、PUT、OPTION 請(qǐng)求

            • 5.1、DELETE 請(qǐng)求

            • 5.2、PUT 請(qǐng)求

            • 5.3、OPTIONS 請(qǐng)求

          • 6、集成 HttpClient

          • 7、集成 okhttp

          • 8、總結(jié)

          • 9、SpringMVC 系列目錄

          • 10、更多好文章

          • 11、【路人甲 Java】所有系列高清 PDF

          1、RestTemplate 概述

          發(fā)送 http 請(qǐng)求,估計(jì)很多人用過 httpclient 和 okhttp,確實(shí)挺好用的,而 Spring web 中的 RestTemplate 和這倆的功能類似,也是用來(lái)發(fā)送 http 請(qǐng)求的,不過用法上面比前面的 2 位要容易很多。

          spring 框架提供的 RestTemplate 類可用于在應(yīng)用中調(diào)用 rest 服務(wù),它簡(jiǎn)化了與 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)行為的簡(jiǎ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),通過案例,把我知道的用法都給盤出來(lái)。

          2、案例代碼

          2.1、git 地址

          https://gitee.com/javacode2018/springmvc-series

          2.2、關(guān)鍵代碼位置

          文中的所有 controller 代碼,在RestTemplateTestController類中。

          所有@Test 用例的代碼,在RestTemplateTest

          2.3、如何運(yùn)行測(cè)試用例?

          • 拉取項(xiàng)目
          • 將 chat16-RestTemplate 模塊發(fā)布到 tomcat9 中
          • 運(yùn)行 RestTemplateTest 中對(duì)應(yīng)的用例即可

          下面咱們來(lái)看 RestTemplate 常見的用法匯總。

          3、發(fā)送 Get 請(qǐng)求

          3.1、普通請(qǐng)求

          接口代碼

          @GetMapping("/test/get")
          @ResponseBody
          public?BookDto?get()?{
          ????return?new?BookDto(1,?"SpringMVC系列");
          }

          使用 RestTemplate 調(diào)用上面這個(gè)接口,通常有 2 種寫法,如下

          @Test
          public?void?test1()?{
          ????RestTemplate?restTemplate?=?new?RestTemplate();
          ????String?url?=?"http://localhost:8080/chat16/test/get";
          ????//getForObject方法,獲取響應(yīng)體,將其轉(zhuǎn)換為第二個(gè)參數(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 中含有動(dòng)態(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)用上面這個(gè)接口,通常有 2 種寫法,如下

          @Test
          public?void?test3()?{
          ????RestTemplate?restTemplate?=?new?RestTemplate();
          ????//url中有動(dòng)態(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中有動(dòng)態(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)接口的返回值為泛型的時(shí)候,這種情況比較特殊,使用 RestTemplate 調(diào)用上面這個(gè)接口,代碼如下,需要用到restTemplate.exchange的方法,這個(gè)方法中有個(gè)參數(shù)是ParameterizedTypeReference類型,通過這個(gè)參數(shù)類指定泛型類型

          @Test
          public?void?test5()?{
          ????RestTemplate?restTemplate?=?new?RestTemplate();
          ????//返回值為泛型
          ????String?url?=?"http://localhost:8080/chat16/test/getList";
          ????//若返回結(jié)果是泛型類型的,需要使用到exchange方法,
          ????//這個(gè)方法中有個(gè)參數(shù)是ParameterizedTypeReference類型,通過這個(gè)參數(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、下載小文件

          接口代碼如下,這個(gè)接口會(huì)下載服務(wù)器端的 1.txt 文件。

          /**
          ?*?下載文件
          ?*
          ?*?@return
          ?*/

          @GetMapping("/test/downFile")
          @ResponseBody
          public?HttpEntity?downFile()?{
          ????//將文件流封裝為InputStreamResource對(duì)象
          ????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è)接口,代碼如下,目前這個(gè)文件的內(nèi)容比較少,可以直接得到一個(gè)數(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);
          }

          注意:如果文件大的時(shí)候,這種方式就有問題了,會(huì)導(dǎo)致 oom,要用下面的方式了。

          3.5、下載大文件

          接口代碼,繼續(xù)使用上面下載 1.txt 的代碼

          /**
          ?*?下載文件
          ?*
          ?*?@return
          ?*/

          @GetMapping("/test/downFile")
          @ResponseBody
          public?HttpEntity?downFile()?{
          ????//將文件流封裝為InputStreamResource對(duì)象
          ????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;
          }

          此時(shí)使用 RestTemplate 調(diào)用這個(gè)接口,代碼如下

          文件比較大的時(shí)候,比如好幾個(gè) G,就不能返回字節(jié)數(shù)組了,會(huì)把內(nèi)存撐爆,導(dǎo)致 OOM,需要使用 execute 方法了,這個(gè)方法中有個(gè) ResponseExtractor 類型的參數(shù),restTemplate 拿到結(jié)果之后,會(huì)回調(diào){@link ResponseExtractor#extractData}這個(gè)方法,在這個(gè)方法中可以拿到響應(yīng)流,然后進(jìn)行處理,這個(gè)過程就是變讀邊處理,不會(huì)導(dǎo)致內(nèi)存溢出

          @Test
          public?void?test7()?{
          ????RestTemplate?restTemplate?=?new?RestTemplate();
          ????String?url?=?"http://localhost:8080/chat16/test/downFile";
          ????/**
          ?????*?文件比較大的時(shí)候,比如好幾個(gè)G,就不能返回字節(jié)數(shù)組了,會(huì)把內(nèi)存撐爆,導(dǎo)致OOM
          ?????*?需要這么玩:
          ?????*?需要使用execute方法了,這個(gè)方法中有個(gè)ResponseExtractor類型的參數(shù),
          ?????*?restTemplate拿到結(jié)果之后,會(huì)回調(diào){@link?ResponseExtractor#extractData}這個(gè)方法,
          ?????*?在這個(gè)方法中可以拿到響應(yīng)流,然后進(jìn)行處理,這個(gè)過程就是變讀邊處理,不會(huì)導(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)用接口,請(qǐng)求頭中傳遞數(shù)據(jù),代碼如下,注意代碼①和②,這兩處是關(guān)鍵,用到了HttpHeadersRequestEntity

          • 請(qǐng)求頭放在 HttpHeaders 對(duì)象中
          • RequestEntity:請(qǐng)求實(shí)體,請(qǐng)求的所有信息都可以放在 RequestEntity 中,比如 body 部分、頭、請(qǐng)求方式、url 等信息
          @Test
          public?void?test8()?{
          ????RestTemplate?restTemplate?=?new?RestTemplate();
          ????String?url?=?"http://localhost:8080/chat16/test/header";
          ????//①:請(qǐng)求頭放在HttpHeaders對(duì)象中
          ????MultiValueMap?headers?=?new?HttpHeaders();
          ????headers.add("header-1",?"V1");
          ????headers.add("header-2",?"Spring");
          ????headers.add("header-2",?"SpringBoot");
          ????//②:RequestEntity:請(qǐng)求實(shí)體,請(qǐng)求的所有信息都可以放在RequestEntity中,比如body部分、頭、請(qǐng)求方式、url等信息
          ????RequestEntity?requestEntity?=?new?RequestEntity(
          ????????????null,?//body部分?jǐn)?shù)據(jù)
          ????????????headers,?//頭
          ????????????HttpMethod.GET,//請(qǐng)求方法
          ????????????URI.create(url)?//地址
          ????);
          ????ResponseEntity>>?responseEntity?=?restTemplate.exchange(requestEntity,
          ????????????new?ParameterizedTypeReference>>()?{
          ????????????});
          ????Map>?result?=?responseEntity.getBody();
          ????System.out.println(result);
          }

          輸出

          {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 動(dòng)態(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 中動(dòng)態(tài)參數(shù)。

          @Test
          public?void?test9()?{
          ????RestTemplate?restTemplate?=?new?RestTemplate();
          ????String?url?=?"http://localhost:8080/chat16/test/getAll/{path1}/{path2}";
          ????//①:請(qǐng)求頭
          ????MultiValueMap?headers?=?new?HttpHeaders();
          ????headers.add("header-1",?"V1");
          ????headers.add("header-2",?"Spring");
          ????headers.add("header-2",?"SpringBoot");
          ????//②:url中的2個(gè)參數(shù)
          ????Map?uriVariables?=?new?HashMap<>();
          ????uriVariables.put("path1",?"v1");
          ????uriVariables.put("path2",?"v2");
          ????//③:HttpEntity:HTTP實(shí)體,內(nèi)部包含了請(qǐng)求頭和請(qǐng)求體
          ????HttpEntity?requestEntity?=?new?HttpEntity(
          ????????null,//body部分,get請(qǐng)求沒有body,所以為null
          ????????headers?//頭
          ????);
          ????//④:使用exchange發(fā)送請(qǐng)求
          ????ResponseEntity>?responseEntity?=?restTemplate.exchange(
          ????????url,?//url
          ????????HttpMethod.GET,?//請(qǐng)求方式
          ????????requestEntity,?//請(qǐng)求實(shí)體(頭、body)
          ????????new?ParameterizedTypeReference>()?{
          ????????},//返回的結(jié)果類型
          ????????uriVariables?//url中的占位符對(duì)應(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 請(qǐng)求

          4.1、post 請(qǐng)求常見的 3 種類型

          http 請(qǐng)求頭中的 Content-Type 用來(lái)指定請(qǐng)求的類型,常見的有 3 種

          Content-Type說(shuō)明
          application/x-www-form-urlencoded頁(yè)面中普通的 form 表單提交時(shí)就是這種類型,表單中的元素會(huì)按照名稱和值拼接好,然后之間用&連接,格式如:p1=v1&p2=v2&p3=v3
          然后通過 urlencoded 編碼之后丟在 body 中發(fā)送
          multipart/form-data頁(yè)面中表單上傳文件的時(shí)候,用到的就是這種格式
          application/json將發(fā)送的數(shù)據(jù)轉(zhuǎn)換為 json 格式,丟在 http 請(qǐng)求的 body 中發(fā)送,后端接口通常用@RequestBody 配合對(duì)象來(lái)接收。

          下面看則種方式的案例。

          4.2、普通表單請(qǐng)求

          普通表單默認(rèn)為 application/x-www-form-urlencoded 類型的請(qǐng)求。

          接口代碼

          @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ā)送請(qǐng)求(url,請(qǐng)求體,返回值需要轉(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系列");
          ????//②:請(qǐng)求頭
          ????HttpHeaders?headers?=?new?HttpHeaders();
          ????//調(diào)用set方法放入請(qǐng)求頭
          ????headers.set(HttpHeaders.CONTENT_TYPE,?MediaType.APPLICATION_FORM_URLENCODED_VALUE);
          ????//③:請(qǐng)求實(shí)體:包含了請(qǐng)求體和請(qǐng)求頭
          ????HttpEntity>?httpEntity?=?new?HttpEntity<>(body,?headers);
          ????//④:發(fā)送請(qǐng)求(url,請(qǐng)求實(shí)體,返回值需要轉(zhuǎn)換的類型)
          ????BookDto?result?=?restTemplate.postForObject(url,?httpEntity,?BookDto.class);
          ????System.out.println(result);
          }

          4.3、上傳本地文件

          上傳文件 Content-Type 為 multipart/form-data 類型。

          接口如下,上傳上傳單個(gè)文件,返回值為一個(gè) 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 來(lái)上傳本地文件,另外 2 種(InputStreamResource、ByteArrayResource)用法就比較特殊了,見下個(gè)案例。

          @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方法放入表單元素(表單名稱:值)
          ????//②:文件對(duì)應(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");
          ????//④:請(qǐng)求實(shí)體
          ????RequestEntity>?requestEntity?=?new?RequestEntity<>(body,?headers,?HttpMethod.POST,?URI.create(url));
          ????//⑤:發(fā)送請(qǐng)求(請(qǐng)求實(shí)體,返回值需要轉(zhuǎn)換的類型)
          ????ResponseEntity>?responseEntity?=?restTemplate.exchange(
          ????????requestEntity,
          ????????new?ParameterizedTypeReference>()?{
          ????????});
          ????Map?result?=?responseEntity.getBody();
          ????System.out.println(result);
          }

          4.4、通過流或字節(jié)數(shù)組的方式上傳文件

          有時(shí)候,上傳的文件是通過流的方式或者字節(jié)數(shù)組的方式,那么就需要用到 InputStreamResource、ByteArrayResource 這倆了。

          **注意:**使用這倆的時(shí)候,需要重寫 2 個(gè)方法,否則會(huì)上傳失敗

          • getFilename:文件名稱
          • contentLength:長(zhǎng)度
          @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個(gè)方法
          ?????* getFilename:文件名稱
          ?????* contentLength:長(zhǎng)度
          ?????*/

          ????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");
          ????//④:請(qǐng)求實(shí)體
          ????RequestEntity>?requestEntity?=?new?RequestEntity<>(body,?headers,?HttpMethod.POST,?URI.create(url));
          ????//⑤:發(fā)送請(qǐng)求(請(qǐng)求實(shí)體,返回值需要轉(zhuǎn)換的類型)
          ????ResponseEntity>?responseEntity?=?restTemplate.exchange(
          ????????????requestEntity,
          ????????????new?ParameterizedTypeReference>()?{
          ????????????});
          ????Map?result?=?responseEntity.getBody();
          ????System.out.println(result);
          }

          4.5、復(fù)雜表單:多個(gè)普通元素+多文件上傳

          接口

          /**
          ?*?復(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:包含了多個(gè)元素(姓名、頭像、多張證件照),這種可以模擬復(fù)雜的表單

          public?class?UserDto?{
          ????//姓名
          ????private?String?name;
          ????//頭像
          ????private?MultipartFile?headImg;
          ????//多張證件照
          ????private?List?idImgList;

          ????//get?set?省略了...
          }

          用 RestTemplate 調(diào)用這個(gè)接口,代碼如下

          @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"));
          ????//來(lái)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");
          ????//④:請(qǐng)求實(shí)體
          ????RequestEntity>?requestEntity?=?new?RequestEntity<>(body,?headers,?HttpMethod.POST,?URI.create(url));
          ????//⑤:發(fā)送請(qǐng)求(請(qǐng)求實(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 對(duì)象

          接口

          /**
          ?*?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 對(duì)象,返回值為泛型

          接口

          /**
          ?*?body中json格式的數(shù)據(jù),返回值為泛型
          ?*
          ?*?@param?bookDtoList
          ?*?@return
          ?*/

          @PostMapping("/test/form5")
          @ResponseBody
          public?List?form5(@RequestBody?List?bookDtoList)?{
          ????return?bookDtoList;
          }

          用 RestTemplate 調(diào)用這個(gè)接口,代碼如下

          @Test
          public?void?test16()?{
          ????RestTemplate?restTemplate?=?new?RestTemplate();
          ????String?url?=?"http://localhost:8080/chat16/test/form5";
          ????//①:請(qǐng)求體,發(fā)送的時(shí)候會(huì)被轉(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");
          ????//③:請(qǐng)求實(shí)體
          ????RequestEntity?requestEntity?=?new?RequestEntity(body,?headers,?HttpMethod.POST,?URI.create(url));
          ????//④:發(fā)送請(qǐng)求(請(qǐng)求實(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 個(gè) json 案例 body 都是 java 對(duì)象,RestTemplate 默認(rèn)自動(dòng)配上 Content-Type=application/json

          但是如果 body 的值是 json 格式字符串的時(shí)候,調(diào)用的時(shí)候需要在頭中明確指定 Content-Type=application/json,寫法如下:

          @Test
          public?void?test17()?{
          ????RestTemplate?restTemplate?=?new?RestTemplate();
          ????String?url?=?"http://localhost:8080/chat16/test/form5";
          ????//①:請(qǐng)求體為一個(gè)json格式的字符串
          ????String?body?=?"[{\"id\":1,\"name\":\"SpringMVC系列\(zhòng)"},{\"id\":2,\"name\":\"MySQL系列\(zhòng)"}]";
          ????/**
          ?????*?②:若請(qǐng)求體為json字符串的時(shí)候,需要在頭中設(shè)置Content-Type=application/json;
          ?????*?若body是普通的java類的時(shí)候,無(wú)需指定這個(gè),RestTemplate默認(rèn)自動(dòng)配上Content-Type=application/json
          ?????*/

          ????HttpHeaders?headers?=?new?HttpHeaders();
          ????headers.setContentType(MediaType.APPLICATION_JSON);
          ????//③:請(qǐng)求實(shí)體(body,頭、請(qǐng)求方式,uri)
          ????RequestEntity?requestEntity?=?new?RequestEntity(body,?headers,?HttpMethod.POST,?URI.create(url));
          ????//④:發(fā)送請(qǐng)求(請(qǐng)求實(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 請(qǐng)求

          5.1、DELETE 請(qǐng)求

          public?void?delete(String?url,?Object...?uriVariables);
          public?void?delete(String?url,?Map?uriVariables);
          public?void?delete(URI?url);

          5.2、PUT 請(qǐng)求

          PUT 請(qǐng)求和 POST 請(qǐng)求類似,將類型改為 PUT 就可以了。

          5.3、OPTIONS 請(qǐng)求

          OPTIONS 請(qǐng)求用來(lái)探測(cè)接口支持哪些 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ā)送請(qǐng)求的,性能上面并不是太突出。

          可以將其替換為 httpclient 或者 okhttp。

          先來(lái)看下如何替換為 HttpClient。

          引入 maven 配置

          <dependency>
          ????<groupId>org.apache.httpcomponentsgroupId>
          ????<artifactId>httpclientartifactId>
          ????<version>4.5.7version>
          dependency>

          創(chuàng)建 RestTemplate 時(shí)指定 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()
          ????????????????//?注冊(cè)http和https請(qǐng)求
          ????????????????.register("http",?PlainConnectionSocketFactory.getSocketFactory())
          ????????????????.register("https",?sslConnectionSocketFactory).build();

          ????????//使用Httpclient連接池的方式配置(推薦),同時(shí)支持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)請(qǐng)求頭
          ????????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í)(毫秒),這里設(shè)置10秒
          ????clientHttpRequestFactory.setConnectTimeout(10?*?1000);
          ????//?數(shù)據(jù)讀取超時(shí)時(shí)間(毫秒),這里設(shè)置60秒
          ????clientHttpRequestFactory.setReadTimeout(60?*?1000);
          ????//?從連接池獲取請(qǐng)求連接的超時(shí)時(shí)間(毫秒),不宜過長(zhǎng),必須設(shè)置,比如連接不夠用時(shí),時(shí)間過長(zhǎng)將是災(zāi)難性的
          ????clientHttpRequestFactory.setConnectionRequestTimeout(10?*?1000);
          ????return?clientHttpRequestFactory;
          }

          public?RestTemplate?restTemplate(){
          ????//創(chuàng)建RestTemplate的時(shí)候,指定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)換為第二個(gè)參數(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 跟蹤一下過程,這樣用起來(lái)就非常順手了。

          尚硅谷 Java 學(xué)科全套教程(總 207.77GB)

          9、SpringMVC 系列目錄

          1. SpringMVC 系列第 1 篇:helloword
          2. SpringMVC 系列第 2 篇:@Controller、@RequestMapping
          3. SpringMVC 系列第 3 篇:異常高效的一款接口測(cè)試?yán)?/a>
          4. SpringMVC 系列第 4 篇:controller 常見的接收參數(shù)的方式
          5. SpringMVC 系列第 5 篇:@RequestBody 大解密,說(shuō)點(diǎn)你不知道的
          6. SpringMVC 系列第 6 篇:上傳文件的 4 種方式,你都會(huì)么?
          7. SpringMVC 系列第 7 篇:SpringMVC 返回視圖常見的 5 種方式,你會(huì)幾種?
          8. SpringMVC 系列第 8 篇:返回 json & 通用返回值設(shè)計(jì)
          9. SpringMVC 系列第 9 篇:SpringMVC 返回 null 是什么意思?
          10. SpringMVC 系列第 10 篇:異步處理
          11. SpringMVC 系列第 11 篇:集成靜態(tài)資源
          12. SpringMVC 系列第 12 篇:攔截器
          13. SpringMVC 系列第 13 篇:統(tǒng)一異常處理
          14. SpringMVC 系列第 14 篇:實(shí)戰(zhàn)篇:通用返回值 & 異常處理設(shè)計(jì)
          15. SpringMVC 系列第 15 篇:全注解的方式 ?&? 原理解析
          16. SpringMVC 系列第 16 篇:通過源碼解析 SpringMVC 處理請(qǐng)求的流程
          17. SpringMVC 系列第 17 篇:源碼解析 SpringMVC 容器的啟動(dòng)過程
          18. SpringMVC 系列第 18 篇:強(qiáng)大的 RequestBodyAdvice 解密
          19. SpringMVC 系列第 19 篇:強(qiáng)大的 ResponseBodyAdvice 解密
          20. SpringMVC 系列第 20 篇:RestFull 詳解

          10、更多好文章

          1. Spring 高手系列(共 56 篇)
          2. Java 高并發(fā)系列(共 34 篇)
          3. MySql 高手系列(共 27 篇)
          4. Maven 高手系列(共 10 篇)
          5. Mybatis 系列(共 12 篇)
          6. 聊聊 db 和緩存一致性常見的實(shí)現(xiàn)方式
          7. 接口冪等性這么重要,它是什么?怎么實(shí)現(xiàn)?
          8. 泛型,有點(diǎn)難度,會(huì)讓很多人懵逼,那是因?yàn)槟銢]有看這篇文章!

          11、【路人甲 Java】所有系列高清 PDF

          領(lǐng)取方式,掃碼發(fā)送:yyds

          瀏覽 101
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  久久国产视频淫 | 免费看一级A沽 | 69久久久久久久 | 九九九999成人 | 影音先锋全部av鲁色资源站小说 |