主頁 > 移動端開發 > 一文吃透介面呼叫神器RestTemplate

一文吃透介面呼叫神器RestTemplate

2021-11-03 08:34:06 移動端開發

文末可以領取所有系列高清 pdf,

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

本文介紹 Spring web 中特別牛逼的一個類 RestTemplate,

目錄

  • 1、RestTemplate 概述

  • 2、案例代碼

    • 2.1、git 地址

    • 2.2、關鍵代碼位置

    • 2.3、如何運行測驗用例?

  • 3、發送 Get 請求

    • 3.1、普通請求

    • 3.2、url 中含有動態引數

    • 3.3、介面回傳值為泛型

    • 3.4、下載小檔案

    • 3.5、下載大檔案

    • 3.6、傳遞頭

    • 3.7、綜合案例:含頭、url 動態引數

  • 4、POST 請求

    • 4.1、post 請求常見的 3 種型別

    • 4.2、普通表單請求

    • 4.3、上傳本地檔案

    • 4.4、通過流或位元組陣列的方式上傳檔案

    • 4.5、復雜表單:多個普通元素+多檔案上傳

    • 4.6、發送 json 格式資料:傳遞 java 物件

    • 4.7、發送 json 格式資料:傳遞 java 物件,回傳值為泛型

    • 4.8、發送 json 字串格式資料

  • 5、DELETE、PUT、OPTION 請求

    • 5.1、DELETE 請求

    • 5.2、PUT 請求

    • 5.3、OPTIONS 請求

  • 6、集成 HttpClient

  • 7、集成 okhttp

  • 8、總結

  • 9、SpringMVC 系列目錄

  • 10、更多好文章

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

1、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()等方法,

下面給大家上案例,案例是重點,通過案例,把我知道的用法都給盤出來,

2、案例代碼

2.1、git 地址

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

10d8584b55c22fe2e509a0a93fc0126a.png

2.2、關鍵代碼位置

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

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

3c0bd3eaa63e802b41c52ec909fae3a5.png

2.3、如何運行測驗用例?

  • 拉取專案

  • 將 chat16-RestTemplate 模塊發布到 tomcat9 中

  • 運行 RestTemplateTest 中對應的用例即可

下面咱們來看 RestTemplate 常見的用法匯總,

3、發送 Get 請求

3.1、普通請求

介面代碼

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

使用 RestTemplate 呼叫上面這個介面,通常有 2 種寫法,如下

@Test
public void test1() {
    RestTemplate restTemplate = new RestTemplate();
    String url = "http://localhost:8080/chat16/test/get";
    //getForObject方法,獲取回應體,將其轉換為第二個引數指定的型別
    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中包含了回應結果中的所有資訊,比如頭、狀態、body
    ResponseEntity<BookDto> responseEntity = restTemplate.getForEntity(url, BookDto.class);
    //狀態碼
    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 中含有動態引數

介面代碼

@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/chat16/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);
    System.out.println(bookDto);
}

@Test
public void test4() {
    RestTemplate restTemplate = new RestTemplate();
    //url中有動態引數
    String url = "http://localhost:8080/chat16/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();
    System.out.println(bookDto);
}

test3 輸出

BookDto{id=1, name='SpringMVC系列'}

test4 輸出

BookDto{id=1, name='SpringMVC系列'}

3.3、介面回傳值為泛型

介面代碼

@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/chat16/test/getList";
    //若回傳結果是泛型型別的,需要使用到exchange方法,
    //這個方法中有個引數是ParameterizedTypeReference型別,通過這個引數類指定泛型型別
    ResponseEntity<List<BookDto>> responseEntity =
            restTemplate.exchange(url,
                    HttpMethod.GET,
                    null,
                    new ParameterizedTypeReference<List<BookDto>>() {
                    });
    List<BookDto> bookDtoList = responseEntity.getBody();
    System.out.println(bookDtoList);
}

輸出

[BookDto{id=1, name='Spring高手系列'}, BookDto{id=2, name='SpringMVC系列'}]

3.4、下載小檔案

介面代碼如下,這個介面會下載服務器端的 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/chat16/test/downFile";
    //檔案比較小的情況,直接回傳位元組陣列
    ResponseEntity<byte[]> responseEntity = restTemplate.getForEntity(url, byte[].class);
    //獲取檔案的內容
    byte[] body = responseEntity.getBody();
    String content = new String(body);
    System.out.println(content);
}

注意:如果檔案大的時候,這種方式就有問題了,會導致 oom,要用下面的方式了,

3.5、下載大檔案

介面代碼,繼續使用上面下載 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 呼叫這個介面,代碼如下

檔案比較大的時候,比如好幾個 G,就不能回傳位元組陣列了,會把記憶體撐爆,導致 OOM,需要使用 execute 方法了,這個方法中有個 ResponseExtractor 型別的引數,restTemplate 拿到結果之后,會回呼{@link ResponseExtractor#extractData}這個方法,在這個方法中可以拿到回應流,然后進行處理,這個程序就是變讀邊處理,不會導致記憶體溢位

@Test
public void test7() {
    RestTemplate restTemplate = new RestTemplate();
    String url = "http://localhost:8080/chat16/test/downFile";
    /**
     * 檔案比較大的時候,比如好幾個G,就不能回傳位元組陣列了,會把記憶體撐爆,導致OOM
     * 需要這么玩:
     * 需要使用execute方法了,這個方法中有個ResponseExtractor型別的引數,
     * restTemplate拿到結果之后,會回呼{@link ResponseExtractor#extractData}這個方法,
     * 在這個方法中可以拿到回應流,然后進行處理,這個程序就是變讀邊處理,不會導致記憶體溢位
     */
    String result = restTemplate.execute(url,
            HttpMethod.GET,
            null,
            new ResponseExtractor<String>() {
                @Override
                public String extractData(ClientHttpResponse response) throws IOException {
                    System.out.println("狀態:"+response.getStatusCode());
                    System.out.println("頭:"+response.getHeaders());
                    //獲取回應體流
                    InputStream body = response.getBody();
                    //處理回應體流
                    String content = IOUtils.toString(body, "UTF-8");
                    return content;
                }
            }, new HashMap<>());

    System.out.println(result);
}

3.6、傳遞頭

介面代碼

@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 呼叫介面,請求頭中傳遞資料,代碼如下,注意代碼①和②,這兩處是關鍵,用到了HttpHeadersRequestEntity

  • 請求頭放在 HttpHeaders 物件中

  • RequestEntity:請求物體,請求的所有資訊都可以放在 RequestEntity 中,比如 body 部分、頭、請求方式、url 等資訊

@Test
public void test8() {
    RestTemplate restTemplate = new RestTemplate();
    String url = "http://localhost:8080/chat16/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();
    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 動態引數

介面

@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/chat16/test/getAll/{path1}/{path2}";
    //①:請求頭
    MultiValueMap<String, String> headers = new HttpHeaders();
    headers.add("header-1", "V1");
    headers.add("header-2", "Spring");
    headers.add("header-2", "SpringBoot");
    //②:url中的2個引數
    Map<String, String> uriVariables = new HashMap<>();
    uriVariables.put("path1", "v1");
    uriVariables.put("path2", "v2");
    //③:HttpEntity:HTTP物體,內部包含了請求頭和請求體
    HttpEntity requestEntity = new HttpEntity(
        null,//body部分,get請求沒有body,所以為null
        headers //頭
    );
    //④:使用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();
    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 中發送
multipart/form-data頁面中表單上傳檔案的時候,用到的就是這種格式
application/json將發送的資料轉換為 json 格式,丟在 http 請求的 body 中發送,后端介面通常用@RequestBody 配合物件來接收,

下面看則種方式的案例,

4.2、普通表單請求

普通表單默認為 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/chat16/test/form1";
    //①:表單資訊,需要放在MultiValueMap中,MultiValueMap相當于Map<String,List<String>>
    MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
    //呼叫add方法填充表單資料(表單名稱:值)
    body.add("id","1");
    body.add("name","SpringMVC系列");
    //②:發送請求(url,請求體,回傳值需要轉換的型別)
    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相當于Map<String,List<String>>
    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);
    System.out.println(result);
}

4.3、上傳本地檔案

上傳檔案 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/chat16/test/form2";
    //①:表單資訊,需要放在MultiValueMap中,MultiValueMap相當于Map<String,List<String>>
    MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
    //呼叫add方法放入表單元素(表單名稱:值)
    //②:檔案對應的型別,需要是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");
    //④:請求物體
    RequestEntity<MultiValueMap<String, Object>> requestEntity = new RequestEntity<>(body, headers, HttpMethod.POST, URI.create(url));
    //⑤:發送請求(請求物體,回傳值需要轉換的型別)
    ResponseEntity<Map<String, String>> responseEntity = restTemplate.exchange(
        requestEntity,
        new ParameterizedTypeReference<Map<String, String>>() {
        });
    Map<String, String> result = responseEntity.getBody();
    System.out.println(result);
}

4.4、通過流或位元組陣列的方式上傳檔案

有時候,上傳的檔案是通過流的方式或者位元組陣列的方式,那么就需要用到 InputStreamResource、ByteArrayResource 這倆了,

**注意:**使用這倆的時候,需要重寫 2 個方法,否則會上傳失敗

  • getFilename:檔案名稱

  • contentLength:長度

@Test
public void test13() {
    RestTemplate restTemplate = new RestTemplate();
    String url = "http://localhost:8080/chat16/test/form2";
    //①:表單資訊,需要放在MultiValueMap中,MultiValueMap相當于Map<String,List<String>>
    MultiValueMap<String, Object> 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");
    //④:請求物體
    RequestEntity<MultiValueMap<String, Object>> requestEntity = new RequestEntity<>(body, headers, HttpMethod.POST, URI.create(url));
    //⑤:發送請求(請求物體,回傳值需要轉換的型別)
    ResponseEntity<Map<String, String>> responseEntity = restTemplate.exchange(
            requestEntity,
            new ParameterizedTypeReference<Map<String, String>>() {
            });
    Map<String, String> result = responseEntity.getBody();
    System.out.println(result);
}

4.5、復雜表單:多個普通元素+多檔案上傳

介面

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

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

    //get set 省略了...
}

用 RestTemplate 呼叫這個介面,代碼如下

@Test
public void test14() {
    RestTemplate restTemplate = new RestTemplate();
    String url = "http://localhost:8080/chat16/test/form3";
    //①:表單資訊,需要放在MultiValueMap中,MultiValueMap相當于Map<String,List<String>>
    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"));
    //③:頭
    HttpHeaders headers = new HttpHeaders();
    headers.add("header1", "v1");
    headers.add("header2", "v2");
    //④:請求物體
    RequestEntity<MultiValueMap<String, Object>> requestEntity = new RequestEntity<>(body, headers, HttpMethod.POST, URI.create(url));
    //⑤:發送請求(請求物體,回傳值需要轉換的型別)
    ResponseEntity<Map<String, String>> responseEntity = restTemplate.exchange(
            requestEntity,
            new ParameterizedTypeReference<Map<String, String>>() {
            });
    Map<String, String> result = responseEntity.getBody();
    System.out.println(result);
}

輸出

{name=路人, headImg=1.jpg, idImgList=[2.jpg, 3.jpg]}

4.6、發送 json 格式資料:傳遞 java 物件

介面

/**
 * 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/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、發送 json 格式資料:傳遞 java 物件,回傳值為泛型

介面

/**
 * 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/chat16/test/form5";
    //①:請求體,發送的時候會被轉換為json格式資料
    List<BookDto> body = Arrays.asList(
            new BookDto(1, "SpringMVC系列"),
            new BookDto(2, "MySQL系列"));
    //②:頭
    HttpHeaders headers = new HttpHeaders();
    headers.add("header1", "v1");
    headers.add("header2", "v2");
    //③:請求物體
    RequestEntity requestEntity = new RequestEntity(body, headers, HttpMethod.POST, URI.create(url));
    //④:發送請求(請求物體,回傳值需要轉換的型別)
    ResponseEntity<List<BookDto>> responseEntity = restTemplate.exchange(
            requestEntity,
            new ParameterizedTypeReference<List<BookDto>>() {
            });
    //⑤:獲取結果
    List<BookDto> result = responseEntity.getBody();
    System.out.println(result);
}

輸出

[BookDto{id=1, name='SpringMVC系列'}, BookDto{id=2, name='MySQL系列'}]

4.8、發送 json 字串格式資料

上面 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/chat16/test/form5";
    //①:請求體為一個json格式的字串
    String body = "[{\"id\":1,\"name\":\"SpringMVC系列\"},{\"id\":2,\"name\":\"MySQL系列\"}]";
    /**
     * ②:若請求體為json字串的時候,需要在頭中設定Content-Type=application/json;
     * 若body是普通的java類的時候,無需指定這個,RestTemplate默認自動配上Content-Type=application/json
     */
    HttpHeaders headers = new HttpHeaders();
    headers.setContentType(MediaType.APPLICATION_JSON);
    //③:請求物體(body,頭、請求方式,uri)
    RequestEntity requestEntity = new RequestEntity(body, headers, HttpMethod.POST, URI.create(url));
    //④:發送請求(請求物體,回傳值需要轉換的型別)
    ResponseEntity<List<BookDto>> responseEntity = restTemplate.exchange(
            requestEntity,
            new ParameterizedTypeReference<List<BookDto>>() {
            });
    //⑤:獲取結果
    List<BookDto> 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<String, ?> uriVariables);
public void delete(URI url);

5.2、PUT 請求

PUT 請求和 POST 請求類似,將型別改為 PUT 就可以了,

5.3、OPTIONS 請求

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);

6、集成 HttpClient

RestTemplate 內部默認用的是 jdk 自帶的 HttpURLConnection 發送請求的,性能上面并不是太突出,

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

先來看下如何替換為 HttpClient,

引入 maven 配置

<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    <version>4.5.7</version>
</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/chat16/test/get";
    //getForObject方法,獲取回應體,將其轉換為第二個引數指定的型別
    BookDto bookDto = restTemplate.getForObject(url, BookDto.class);
    System.out.println(bookDto);
}

7、集成 okhttp

引入 maven 配置

<dependency>
    <groupId>com.squareup.okhttp3</groupId>
    <artifactId>okhttp</artifactId>
    <version>4.3.1</version>
</dependency>

創建 RestTemplate

new RestTemplate(new OkHttp3ClientHttpRequestFactory());

8、總結

RestTemplate 使用確實非常容易,建議大家去看一下 RestTemplate 的原始碼,debug 跟蹤一下程序,這樣用起來就非常順手了,

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

9、SpringMVC 系列目錄

  1. SpringMVC 系列第 1 篇:helloword

  2. SpringMVC 系列第 2 篇:@Controller、@RequestMapping

  3. SpringMVC 系列第 3 篇:例外高效的一款介面測驗利器

  4. SpringMVC 系列第 4 篇:controller 常見的接收引數的方式

  5. SpringMVC 系列第 5 篇:@RequestBody 大解密,說點你不知道的

  6. SpringMVC 系列第 6 篇:上傳檔案的 4 種方式,你都會么?

  7. SpringMVC 系列第 7 篇:SpringMVC 回傳視圖常見的 5 種方式,你會幾種?

  8. SpringMVC 系列第 8 篇:回傳 json & 通用回傳值設計

  9. SpringMVC 系列第 9 篇:SpringMVC 回傳 null 是什么意思?

  10. SpringMVC 系列第 10 篇:異步處理

  11. SpringMVC 系列第 11 篇:集成靜態資源

  12. SpringMVC 系列第 12 篇:攔截器

  13. SpringMVC 系列第 13 篇:統一例外處理

  14. SpringMVC 系列第 14 篇:實戰篇:通用回傳值 & 例外處理設計

  15. SpringMVC 系列第 15 篇:全注解的方式 & 原理決議

  16. SpringMVC 系列第 16 篇:通過原始碼決議 SpringMVC 處理請求的流程

  17. SpringMVC 系列第 17 篇:原始碼決議 SpringMVC 容器的啟動程序

  18. SpringMVC 系列第 18 篇:強大的 RequestBodyAdvice 解密

  19. SpringMVC 系列第 19 篇:強大的 ResponseBodyAdvice 解密

  20. SpringMVC 系列第 20 篇:RestFull 詳解

10、更多好文章

  1. Spring 高手系列(共 56 篇)

  2. Java 高并發系列(共 34 篇)

  3. MySql 高手系列(共 27 篇)

  4. Maven 高手系列(共 10 篇)

  5. Mybatis 系列(共 12 篇)

  6. 聊聊 db 和快取一致性常見的實作方式

  7. 介面冪等性這么重要,它是什么?怎么實作?

  8. 泛型,有點難度,會讓很多人懵逼,那是因為你沒有看這篇文章!

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

領取方式,掃碼發送:yyds

84e19fb78bd1cedc2cba886ea5c699bc.png

轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/345786.html

標籤:其他

上一篇:OkHttp快取篇

下一篇:iOS15適配本地通知功能

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【從零開始擼一個App】Dagger2

    Dagger2是一個IOC框架,一般用于Android平臺,第一次接觸的朋友,一定會被搞得暈頭轉向。它延續了Java平臺Spring框架代碼碎片化,注解滿天飛的傳統。嘗試將各處代碼片段串聯起來,理清思緒,真不是件容易的事。更不用說還有各版本細微的差別。 與Spring不同的是,Spring是通過反射 ......

    uj5u.com 2020-09-10 06:57:59 more
  • Flutter Weekly Issue 66

    新聞 Flutter 季度調研結果分享 教程 Flutter+FaaS一體化任務編排的思考與設計 詳解Dart中如何通過注解生成代碼 GitHub 用對了嗎?Flutter 團隊分享如何管理大型開源專案 插件 flutter-bubble-tab-indicator A Flutter librar ......

    uj5u.com 2020-09-10 06:58:52 more
  • Proguard 常用規則

    介紹 Proguard 入口,如何查看輸出,如何使用 keep 設定入口以及使用實體,如何配置壓縮,混淆,校驗等規則。

    ......

    uj5u.com 2020-09-10 06:59:00 more
  • Android 開發技術周報 Issue#292

    新聞 Android即將獲得類AirDrop功能:可向附近設備快速分享檔案 谷歌為安卓檔案管理應用引入可安全隱藏資料的Safe Folder功能 Android TV新主界面將顯示電影、電視節目和應用推薦內容 泄露的Android檔案暗示了傳說中的谷歌Pixel 5a與折疊屏新機 谷歌發布Andro ......

    uj5u.com 2020-09-10 07:00:37 more
  • AutoFitTextureView Error inflating class

    報錯: Binary XML file line #0: Binary XML file line #0: Error inflating class xxx.AutoFitTextureView 解決: <com.example.testy2.AutoFitTextureView android: ......

    uj5u.com 2020-09-10 07:00:41 more
  • 根據Uri,Cursor沒有獲取到對應的屬性

    Android: 背景:呼叫攝像頭,拍攝視頻,指定保存的地址,但是回傳的Cursor檔案,只有名稱和大小的屬性,沒有其他諸如時長,連ID屬性都沒有 使用 cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DURATIO ......

    uj5u.com 2020-09-10 07:00:44 more
  • Android連載29-持久化技術

    一、持久化技術 我們平時所使用的APP產生的資料,在記憶體中都是瞬時的,會隨著斷電、關機等丟失資料,因此android系統采用了持久化技術,用于存盤這些“瞬時”資料 持久化技術包括:檔案存盤、SharedPreference存盤以及資料庫存盤,還有更復雜的SD卡記憶體儲。 二、檔案存盤 最基本存盤方式, ......

    uj5u.com 2020-09-10 07:00:47 more
  • Android Camera2Video整合到自己專案里

    背景: Android專案里呼叫攝像頭拍攝視頻,原本使用的 MediaStore.ACTION_VIDEO_CAPTURE, 后來因專案需要,改成了camera2 1.Camera2Video 官方demo有點問題,下載后,不能直接整合到專案 問題1.多次拍攝視頻崩潰 問題2.雙擊record按鈕, ......

    uj5u.com 2020-09-10 07:00:50 more
  • Android 開發技術周報 Issue#293

    新聞 谷歌為Android TV開發者提供多種新功能 Android 11將自動填表功能整合到鍵盤輸入建議中 谷歌宣布Android Auto即將支持更多的導航和數字停車應用 谷歌Pixel 5只有XL版本 搭載驍龍765G且將比Pixel 4更便宜 [圖]Wear OS將迎來重磅更新:應用啟動時間 ......

    uj5u.com 2020-09-10 07:01:38 more
  • 海豚星空掃碼投屏 Android 接收端 SDK 集成 六步驟

    掃碼投屏,開放網路,獨占設備,不需要額外下載軟體,微信掃碼,發現設備。支持標準DLNA協議,支持倍速播放。視頻,音頻,圖片投屏。好點意思。還支持自定義基于 DLNA 擴展的操作動作。好像要收費,沒體驗。 這里簡單記錄一下集成程序。 一 跟目錄的build.gradle添加私有mevan倉庫 mave ......

    uj5u.com 2020-09-10 07:01:43 more
最新发布
  • 歡迎頁輪播影片

    如圖,引導開始,球從上落下,同時淡入文字,然后文字開始輪播,最后一頁時停止,點擊進入首頁。 在來看看效果圖。 重力球先不講,主要歡迎輪播簡單實作 首先新建一個類 TextTranslationXGuideView,用于影片展示 文本是類似的,最后會有個圖片箭頭影片,布局很簡單,就是一個 TextVi ......

    uj5u.com 2023-04-20 08:40:31 more
  • 【FAQ】關于華為推送服務因營銷訊息頻次管控導致服務通訊類訊息

    一. 問題描述 使用華為推送服務下發IM訊息時,下發訊息請求成功且code碼為80000000,但是手機總是收不到訊息; 在華為推送自助分析(Beta)平臺查看發現,訊息發送觸發了頻控。 二. 問題原因及背景 2023年1月05日起,華為推送服務對咨詢營銷類訊息做了單個設備每日推送數量上限管理,具體 ......

    uj5u.com 2023-04-20 08:40:11 more
  • 歡迎頁輪播影片

    如圖,引導開始,球從上落下,同時淡入文字,然后文字開始輪播,最后一頁時停止,點擊進入首頁。 在來看看效果圖。 重力球先不講,主要歡迎輪播簡單實作 首先新建一個類 TextTranslationXGuideView,用于影片展示 文本是類似的,最后會有個圖片箭頭影片,布局很簡單,就是一個 TextVi ......

    uj5u.com 2023-04-20 08:39:36 more
  • 【FAQ】關于華為推送服務因營銷訊息頻次管控導致服務通訊類訊息

    一. 問題描述 使用華為推送服務下發IM訊息時,下發訊息請求成功且code碼為80000000,但是手機總是收不到訊息; 在華為推送自助分析(Beta)平臺查看發現,訊息發送觸發了頻控。 二. 問題原因及背景 2023年1月05日起,華為推送服務對咨詢營銷類訊息做了單個設備每日推送數量上限管理,具體 ......

    uj5u.com 2023-04-20 08:39:13 more
  • iOS從UI記憶體地址到讀取成員變數(oc/swift)

    開發除錯時,我們發現bug時常首先是從UI顯示發現例外,下一步才會去定位UI相關連的資料的。XCode有給我們提供一系列debug工具,但是很多人可能還沒有形成一套穩定的除錯流程,因此本文嘗試解決這個問題,順便提出一個暴論:UI顯示例外問題只需要兩個步驟就能完成定位作業的80%: 定位例外 UI 組 ......

    uj5u.com 2023-04-19 09:16:23 more
  • FIDE重磅更新!性能飛躍!體驗有禮!

    FIDE 開發者工具重構升級啦!實作500%性能提升,誠邀體驗! 一直以來不少開發者朋友在社區反饋,在使用 FIDE 工具的程序中,時常會遇到諸如加載不及時、代碼預覽/渲染性能不如意的情況,十分影響開發體驗。 作為技術團隊,我們深知一件趁手的開發工具對開發者的重要性,因此,在2023年開年,FinC ......

    uj5u.com 2023-04-19 09:16:15 more
  • 游戲內嵌社區服務開放,助力開發者提升玩家互動與留存

    華為 HMS Core 游戲內嵌社區服務提供快速訪問華為游戲中心論壇能力,支持玩家直接在游戲內瀏覽帖子和交流互動,助力開發者擴展內容生產和觸達的場景。 一、為什么要游戲內嵌社區? 二、游戲內嵌社區的典型使用場景 1、游戲內打開論壇 您可以在游戲內繪制論壇入口,為玩家提供沉浸式發帖、瀏覽、點贊、回帖、 ......

    uj5u.com 2023-04-19 09:15:46 more
  • iOS從UI記憶體地址到讀取成員變數(oc/swift)

    開發除錯時,我們發現bug時常首先是從UI顯示發現例外,下一步才會去定位UI相關連的資料的。XCode有給我們提供一系列debug工具,但是很多人可能還沒有形成一套穩定的除錯流程,因此本文嘗試解決這個問題,順便提出一個暴論:UI顯示例外問題只需要兩個步驟就能完成定位作業的80%: 定位例外 UI 組 ......

    uj5u.com 2023-04-19 09:14:53 more
  • FIDE重磅更新!性能飛躍!體驗有禮!

    FIDE 開發者工具重構升級啦!實作500%性能提升,誠邀體驗! 一直以來不少開發者朋友在社區反饋,在使用 FIDE 工具的程序中,時常會遇到諸如加載不及時、代碼預覽/渲染性能不如意的情況,十分影響開發體驗。 作為技術團隊,我們深知一件趁手的開發工具對開發者的重要性,因此,在2023年開年,FinC ......

    uj5u.com 2023-04-19 09:14:08 more
  • 游戲內嵌社區服務開放,助力開發者提升玩家互動與留存

    華為 HMS Core 游戲內嵌社區服務提供快速訪問華為游戲中心論壇能力,支持玩家直接在游戲內瀏覽帖子和交流互動,助力開發者擴展內容生產和觸達的場景。 一、為什么要游戲內嵌社區? 二、游戲內嵌社區的典型使用場景 1、游戲內打開論壇 您可以在游戲內繪制論壇入口,為玩家提供沉浸式發帖、瀏覽、點贊、回帖、 ......

    uj5u.com 2023-04-19 09:08:34 more