RestTemplate接口調用神器常見用法匯總

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,寫法如下:

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

輸出

[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 跟蹤一下過程,這樣用起來就非常順手瞭。

到此這篇關於RestTemplate接口調用神器常見用法匯總的文章就介紹到這瞭,更多相關RestTemplate接口調用內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: