SpringMVC系列第21篇:接口调用利器RestTemplate

star2017 1年前 ⋅ 332 阅读

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

本文介绍Spring web中特别牛逼的一个类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地址

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

2.2、关键代码位置

文中的所有controller代码,在RestTemplateTestController类中。

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

2.3、如何运行测试用例?

  • 拉取项目
  • 将chat16-RestTemplate模块发布到tomcat9中
  • 运行RestTemplateTest中对应的用例即可

下面咱们来看RestTemplate常见的用法汇总。

3、发送Get请求

3.1、普通请求

接口代码

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

使用RestTemplate调用上面这个接口,通常有2种写法,如下

  1. @Test
  2. public void test1() {
  3. RestTemplate restTemplate = new RestTemplate();
  4. String url = "http://localhost:8080/chat16/test/get";
  5. //getForObject方法,获取响应体,将其转换为第二个参数指定的类型
  6. BookDto bookDto = restTemplate.getForObject(url, BookDto.class);
  7. System.out.println(bookDto);
  8. }
  9. @Test
  10. public void test2() {
  11. RestTemplate restTemplate = new RestTemplate();
  12. String url = "http://localhost:8080/chat16/test/get";
  13. //getForEntity方法,返回值为ResponseEntity类型
  14. // ResponseEntity中包含了响应结果中的所有信息,比如头、状态、body
  15. ResponseEntity<BookDto> responseEntity = restTemplate.getForEntity(url, BookDto.class);
  16. //状态码
  17. System.out.println(responseEntity.getStatusCode());
  18. //获取头
  19. System.out.println("头:" + responseEntity.getHeaders());
  20. //获取body
  21. BookDto bookDto = responseEntity.getBody();
  22. System.out.println(bookDto);
  23. }

test1输出

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

test2输出

  1. 200 OK
  2. 头:[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"]
  3. BookDto{id=1, name='SpringMVC系列'}

3.2、url中含有动态参数

接口代码

  1. @GetMapping("/test/get/{id}/{name}")
  2. @ResponseBody
  3. public BookDto get(@PathVariable("id") Integer id, @PathVariable("name") String name) {
  4. return new BookDto(id, name);
  5. }

使用RestTemplate调用上面这个接口,通常有2种写法,如下

  1. @Test
  2. public void test3() {
  3. RestTemplate restTemplate = new RestTemplate();
  4. //url中有动态参数
  5. String url = "http://localhost:8080/chat16/test/get/{id}/{name}";
  6. Map<String, String> uriVariables = new HashMap<>();
  7. uriVariables.put("id", "1");
  8. uriVariables.put("name", "SpringMVC系列");
  9. //使用getForObject或者getForEntity方法
  10. BookDto bookDto = restTemplate.getForObject(url, BookDto.class, uriVariables);
  11. System.out.println(bookDto);
  12. }
  13. @Test
  14. public void test4() {
  15. RestTemplate restTemplate = new RestTemplate();
  16. //url中有动态参数
  17. String url = "http://localhost:8080/chat16/test/get/{id}/{name}";
  18. Map<String, String> uriVariables = new HashMap<>();
  19. uriVariables.put("id", "1");
  20. uriVariables.put("name", "SpringMVC系列");
  21. //getForEntity方法
  22. ResponseEntity<BookDto> responseEntity = restTemplate.getForEntity(url, BookDto.class, uriVariables);
  23. BookDto bookDto = responseEntity.getBody();
  24. System.out.println(bookDto);
  25. }

test3输出

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

test4输出

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

3.3、接口返回值为泛型

接口代码

  1. @GetMapping("/test/getList")
  2. @ResponseBody
  3. public List<BookDto> getList() {
  4. return Arrays.asList(
  5. new BookDto(1, "Spring高手系列"),
  6. new BookDto(2, "SpringMVC系列")
  7. );
  8. }

当接口的返回值为泛型的时候,这种情况比较特殊,使用RestTemplate调用上面这个接口,代码如下,需要用到restTemplate.exchange的方法,这个方法中有个参数是ParameterizedTypeReference类型,通过这个参数类指定泛型类型

  1. @Test
  2. public void test5() {
  3. RestTemplate restTemplate = new RestTemplate();
  4. //返回值为泛型
  5. String url = "http://localhost:8080/chat16/test/getList";
  6. //若返回结果是泛型类型的,需要使用到exchange方法,
  7. //这个方法中有个参数是ParameterizedTypeReference类型,通过这个参数类指定泛型类型
  8. ResponseEntity<List<BookDto>> responseEntity =
  9. restTemplate.exchange(url,
  10. HttpMethod.GET,
  11. null,
  12. new ParameterizedTypeReference<List<BookDto>>() {
  13. });
  14. List<BookDto> bookDtoList = responseEntity.getBody();
  15. System.out.println(bookDtoList);
  16. }

输出

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

3.4、下载小文件

接口代码如下,这个接口会下载服务器端的1.txt文件。

  1. /**
  2. * 下载文件
  3. *
  4. * @return
  5. */
  6. @GetMapping("/test/downFile")
  7. @ResponseBody
  8. public HttpEntity<InputStreamResource> downFile() {
  9. //将文件流封装为InputStreamResource对象
  10. InputStream inputStream = this.getClass().getResourceAsStream("/1.txt");
  11. InputStreamResource inputStreamResource = new InputStreamResource(inputStream);
  12. //设置header
  13. MultiValueMap<String, String> headers = new HttpHeaders();
  14. headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=1.txt");
  15. HttpEntity<InputStreamResource> httpEntity = new HttpEntity<>(inputStreamResource);
  16. return httpEntity;
  17. }

使用RestTemplate调用这个接口,代码如下,目前这个文件的内容比较少,可以直接得到一个数组。

  1. @Test
  2. public void test6() {
  3. RestTemplate restTemplate = new RestTemplate();
  4. String url = "http://localhost:8080/chat16/test/downFile";
  5. //文件比较小的情况,直接返回字节数组
  6. ResponseEntity<byte[]> responseEntity = restTemplate.getForEntity(url, byte[].class);
  7. //获取文件的内容
  8. byte[] body = responseEntity.getBody();
  9. String content = new String(body);
  10. System.out.println(content);
  11. }

注意:如果文件大的时候,这种方式就有问题了,会导致oom,要用下面的方式了。

3.5、下载大文件

接口代码,继续使用上面下载1.txt的代码

  1. /**
  2. * 下载文件
  3. *
  4. * @return
  5. */
  6. @GetMapping("/test/downFile")
  7. @ResponseBody
  8. public HttpEntity<InputStreamResource> downFile() {
  9. //将文件流封装为InputStreamResource对象
  10. InputStream inputStream = this.getClass().getResourceAsStream("/1.txt");
  11. InputStreamResource inputStreamResource = new InputStreamResource(inputStream);
  12. //设置header
  13. MultiValueMap<String, String> headers = new HttpHeaders();
  14. headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=1.txt");
  15. HttpEntity<InputStreamResource> httpEntity = new HttpEntity<>(inputStreamResource);
  16. return httpEntity;
  17. }

此时使用RestTemplate调用这个接口,代码如下

文件比较大的时候,比如好几个G,就不能返回字节数组了,会把内存撑爆,导致OOM,需要使用execute方法了,这个方法中有个ResponseExtractor类型的参数,restTemplate拿到结果之后,会回调{@link ResponseExtractor#extractData}这个方法,在这个方法中可以拿到响应流,然后进行处理,这个过程就是变读边处理,不会导致内存溢出

  1. @Test
  2. public void test7() {
  3. RestTemplate restTemplate = new RestTemplate();
  4. String url = "http://localhost:8080/chat16/test/downFile";
  5. /**
  6. * 文件比较大的时候,比如好几个G,就不能返回字节数组了,会把内存撑爆,导致OOM
  7. * 需要这么玩:
  8. * 需要使用execute方法了,这个方法中有个ResponseExtractor类型的参数,
  9. * restTemplate拿到结果之后,会回调{@link ResponseExtractor#extractData}这个方法,
  10. * 在这个方法中可以拿到响应流,然后进行处理,这个过程就是变读边处理,不会导致内存溢出
  11. */
  12. String result = restTemplate.execute(url,
  13. HttpMethod.GET,
  14. null,
  15. new ResponseExtractor<String>() {
  16. @Override
  17. public String extractData(ClientHttpResponse response) throws IOException {
  18. System.out.println("状态:"+response.getStatusCode());
  19. System.out.println("头:"+response.getHeaders());
  20. //获取响应体流
  21. InputStream body = response.getBody();
  22. //处理响应体流
  23. String content = IOUtils.toString(body, "UTF-8");
  24. return content;
  25. }
  26. }, new HashMap<>());
  27. System.out.println(result);
  28. }

3.6、传递头

接口代码

  1. @GetMapping("/test/header")
  2. @ResponseBody
  3. public Map<String, List<String>> header(HttpServletRequest request) {
  4. Map<String, List<String>> header = new LinkedHashMap<>();
  5. Enumeration<String> headerNames = request.getHeaderNames();
  6. while (headerNames.hasMoreElements()) {
  7. String name = headerNames.nextElement();
  8. Enumeration<String> values = request.getHeaders(name);
  9. List<String> list = new ArrayList<>();
  10. while (values.hasMoreElements()) {
  11. list.add(values.nextElement());
  12. }
  13. header.put(name, list);
  14. }
  15. return header;
  16. }

使用RestTemplate调用接口,请求头中传递数据,代码如下,注意代码①和②,这两处是关键,用到了HttpHeadersRequestEntity

  • 请求头放在HttpHeaders对象中
  • RequestEntity:请求实体,请求的所有信息都可以放在RequestEntity中,比如body部分、头、请求方式、url等信息
  1. @Test
  2. public void test8() {
  3. RestTemplate restTemplate = new RestTemplate();
  4. String url = "http://localhost:8080/chat16/test/header";
  5. //①:请求头放在HttpHeaders对象中
  6. MultiValueMap<String, String> headers = new HttpHeaders();
  7. headers.add("header-1", "V1");
  8. headers.add("header-2", "Spring");
  9. headers.add("header-2", "SpringBoot");
  10. //②:RequestEntity:请求实体,请求的所有信息都可以放在RequestEntity中,比如body部分、头、请求方式、url等信息
  11. RequestEntity requestEntity = new RequestEntity(
  12. null, //body部分数据
  13. headers, //头
  14. HttpMethod.GET,//请求方法
  15. URI.create(url) //地址
  16. );
  17. ResponseEntity<Map<String, List<String>>> responseEntity = restTemplate.exchange(requestEntity,
  18. new ParameterizedTypeReference<Map<String, List<String>>>() {
  19. });
  20. Map<String, List<String>> result = responseEntity.getBody();
  21. System.out.println(result);
  22. }

输出

  1. {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动态参数

接口

  1. @GetMapping("/test/getAll/{path1}/{path2}")
  2. @ResponseBody
  3. public Map<String, Object> getAll(@PathVariable("path1") String path1,
  4. @PathVariable("path2") String path2,
  5. HttpServletRequest request) {
  6. Map<String, Object> result = new LinkedHashMap<>();
  7. result.put("path1", path1);
  8. result.put("path2", path2);
  9. //头
  10. Map<String, List<String>> header = new LinkedHashMap<>();
  11. Enumeration<String> headerNames = request.getHeaderNames();
  12. while (headerNames.hasMoreElements()) {
  13. String name = headerNames.nextElement();
  14. Enumeration<String> values = request.getHeaders(name);
  15. List<String> list = new ArrayList<>();
  16. while (values.hasMoreElements()) {
  17. list.add(values.nextElement());
  18. }
  19. header.put(name, list);
  20. }
  21. result.put("header", header);
  22. return result;
  23. }

如下,使用RestTemplate调用接口,GET方式、传递header、path中动态参数。

  1. @Test
  2. public void test9() {
  3. RestTemplate restTemplate = new RestTemplate();
  4. String url = "http://localhost:8080/chat16/test/getAll/{path1}/{path2}";
  5. //①:请求头
  6. MultiValueMap<String, String> headers = new HttpHeaders();
  7. headers.add("header-1", "V1");
  8. headers.add("header-2", "Spring");
  9. headers.add("header-2", "SpringBoot");
  10. //②:url中的2个参数
  11. Map<String, String> uriVariables = new HashMap<>();
  12. uriVariables.put("path1", "v1");
  13. uriVariables.put("path2", "v2");
  14. //③:HttpEntity:HTTP实体,内部包含了请求头和请求体
  15. HttpEntity requestEntity = new HttpEntity(
  16. null,//body部分,get请求没有body,所以为null
  17. headers //头
  18. );
  19. //④:使用exchange发送请求
  20. ResponseEntity<Map<String, Object>> responseEntity = restTemplate.exchange(
  21. url, //url
  22. HttpMethod.GET, //请求方式
  23. requestEntity, //请求实体(头、body)
  24. new ParameterizedTypeReference<Map<String, Object>>() {
  25. },//返回的结果类型
  26. uriVariables //url中的占位符对应的值
  27. );
  28. Map<String, Object> result = responseEntity.getBody();
  29. System.out.println(result);
  30. }

输出

  1. {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类型的请求。

接口代码

  1. @PostMapping("/test/form1")
  2. @ResponseBody
  3. public BookDto form1(BookDto bookDto) {
  4. return bookDto;
  5. }

使用RestTemplate调用接口

  1. @Test
  2. public void test10() {
  3. RestTemplate restTemplate = new RestTemplate();
  4. String url = "http://localhost:8080/chat16/test/form1";
  5. //①:表单信息,需要放在MultiValueMap中,MultiValueMap相当于Map<String,List<String>>
  6. MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
  7. //调用add方法填充表单数据(表单名称:值)
  8. body.add("id","1");
  9. body.add("name","SpringMVC系列");
  10. //②:发送请求(url,请求体,返回值需要转换的类型)
  11. BookDto result = restTemplate.postForObject(url, body, BookDto.class);
  12. System.out.println(result);
  13. }

如果想携带头信息,代码如下

  1. @Test
  2. public void test11() {
  3. RestTemplate restTemplate = new RestTemplate();
  4. String url = "http://localhost:8080/chat16/test/form1";
  5. //①:表单信息,需要放在MultiValueMap中,MultiValueMap相当于Map<String,List<String>>
  6. MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
  7. //调用add方法放入表单元素(表单名称:值)
  8. body.add("id","1");
  9. body.add("name","SpringMVC系列");
  10. //②:请求头
  11. HttpHeaders headers = new HttpHeaders();
  12. //调用set方法放入请求头
  13. headers.set(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE);
  14. //③:请求实体:包含了请求体和请求头
  15. HttpEntity<MultiValueMap<String, String>> httpEntity = new HttpEntity<>(body, headers);
  16. //④:发送请求(url,请求实体,返回值需要转换的类型)
  17. BookDto result = restTemplate.postForObject(url, httpEntity, BookDto.class);
  18. System.out.println(result);
  19. }

4.3、上传本地文件

上传文件Content-Type为multipart/form-data 类型。

接口如下,上传上传单个文件,返回值为一个Map类型,是泛型类型

  1. @PostMapping(value = "/test/form2")
  2. @ResponseBody
  3. public Map<String, String> form2(@RequestParam("file1") MultipartFile file1) {
  4. Map<String, String> fileMetadata = new LinkedHashMap<>();
  5. fileMetadata.put("文件名", file1.getOriginalFilename());
  6. fileMetadata.put("文件类型", file1.getContentType());
  7. fileMetadata.put("文件大小(byte)", String.valueOf(file1.getSize()));
  8. return fileMetadata;
  9. }

使用RestTemplate调用接口,主要下面代码上传的文件需要包装为org.springframework.core.io.Resource,常用的有3中[FileSystemResource、InputStreamResource、ByteArrayResource],这里案例中我们用到的是FileSystemResource来上传本地文件,另外2种(InputStreamResource、ByteArrayResource)用法就比较特殊了,见下个案例。

  1. @Test
  2. public void test12() {
  3. RestTemplate restTemplate = new RestTemplate();
  4. String url = "http://localhost:8080/chat16/test/form2";
  5. //①:表单信息,需要放在MultiValueMap中,MultiValueMap相当于Map<String,List<String>>
  6. MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
  7. //调用add方法放入表单元素(表单名称:值)
  8. //②:文件对应的类型,需要是org.springframework.core.io.Resource类型的,常见的有[FileSystemResource、InputStreamResource、ByteArrayResource]
  9. body.add("file1", new FileSystemResource(".\\src\\main\\java\\com\\javacode2018\\springmvc\\chat16\\dto\\UserDto.java"));
  10. //③:头
  11. HttpHeaders headers = new HttpHeaders();
  12. headers.add("header1", "v1");
  13. headers.add("header2", "v2");
  14. //④:请求实体
  15. RequestEntity<MultiValueMap<String, Object>> requestEntity = new RequestEntity<>(body, headers, HttpMethod.POST, URI.create(url));
  16. //⑤:发送请求(请求实体,返回值需要转换的类型)
  17. ResponseEntity<Map<String, String>> responseEntity = restTemplate.exchange(
  18. requestEntity,
  19. new ParameterizedTypeReference<Map<String, String>>() {
  20. });
  21. Map<String, String> result = responseEntity.getBody();
  22. System.out.println(result);
  23. }

4.4、通过流或字节数组的方式上传文件

有时候,上传的文件是通过流的方式或者字节数组的方式,那么就需要用到InputStreamResource、ByteArrayResource这俩了。

注意:使用这俩的时候,需要重写2个方法,否则会上传失败

  • getFilename:文件名称
  • contentLength:长度
  1. @Test
  2. public void test13() {
  3. RestTemplate restTemplate = new RestTemplate();
  4. String url = "http://localhost:8080/chat16/test/form2";
  5. //①:表单信息,需要放在MultiValueMap中,MultiValueMap相当于Map<String,List<String>>
  6. MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
  7. /**
  8. * ②:通过流的方式上传文件,流的方式需要用到InputStreamResource类,需要重写2个方法
  9. * getFilename:文件名称
  10. * contentLength:长度
  11. */
  12. InputStream inputStream = RestTemplateTest.class.getResourceAsStream("/1.txt");
  13. InputStreamResource inputStreamResource = new InputStreamResource(inputStream) {
  14. @Override
  15. public String getFilename() {
  16. return "1.txt";
  17. }
  18. @Override
  19. public long contentLength() throws IOException {
  20. return inputStream.available();
  21. }
  22. };
  23. body.add("file1", inputStreamResource);
  24. //③:头
  25. HttpHeaders headers = new HttpHeaders();
  26. headers.add("header1", "v1");
  27. headers.add("header2", "v2");
  28. //④:请求实体
  29. RequestEntity<MultiValueMap<String, Object>> requestEntity = new RequestEntity<>(body, headers, HttpMethod.POST, URI.create(url));
  30. //⑤:发送请求(请求实体,返回值需要转换的类型)
  31. ResponseEntity<Map<String, String>> responseEntity = restTemplate.exchange(
  32. requestEntity,
  33. new ParameterizedTypeReference<Map<String, String>>() {
  34. });
  35. Map<String, String> result = responseEntity.getBody();
  36. System.out.println(result);
  37. }

4.5、复杂表单:多个普通元素+多文件上传

接口

  1. /**
  2. * 复杂的表单:包含了普通元素、多文件
  3. *
  4. * @param userDto
  5. * @return
  6. */
  7. @PostMapping("/test/form3")
  8. @ResponseBody
  9. public Map<String, String> form3(UserDto userDto) {
  10. Map<String, String> result = new LinkedHashMap<>();
  11. result.put("name", userDto.getName());
  12. result.put("headImg", userDto.getHeadImg().getOriginalFilename());
  13. result.put("idImgList", Arrays.toString(userDto.getIdImgList().stream().
  14. map(MultipartFile::getOriginalFilename).toArray()));
  15. return result;
  16. }

UserDto:包含了多个元素(姓名、头像、多张证件照),这种可以模拟复杂的表单

  1. public class UserDto {
  2. //姓名
  3. private String name;
  4. //头像
  5. private MultipartFile headImg;
  6. //多张证件照
  7. private List<MultipartFile> idImgList;
  8. //get set 省略了...
  9. }

用RestTemplate调用这个接口,代码如下

  1. @Test
  2. public void test14() {
  3. RestTemplate restTemplate = new RestTemplate();
  4. String url = "http://localhost:8080/chat16/test/form3";
  5. //①:表单信息,需要放在MultiValueMap中,MultiValueMap相当于Map<String,List<String>>
  6. MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
  7. body.add("name", "路人");
  8. body.add("headImg", new FileSystemResource(".\\src\\main\\resources\\1.jpg"));
  9. //来2张证件照,元素名称一样
  10. body.add("idImgList", new FileSystemResource(".\\src\\main\\resources\\2.jpg"));
  11. body.add("idImgList", new FileSystemResource(".\\src\\main\\resources\\3.jpg"));
  12. //③:头
  13. HttpHeaders headers = new HttpHeaders();
  14. headers.add("header1", "v1");
  15. headers.add("header2", "v2");
  16. //④:请求实体
  17. RequestEntity<MultiValueMap<String, Object>> requestEntity = new RequestEntity<>(body, headers, HttpMethod.POST, URI.create(url));
  18. //⑤:发送请求(请求实体,返回值需要转换的类型)
  19. ResponseEntity<Map<String, String>> responseEntity = restTemplate.exchange(
  20. requestEntity,
  21. new ParameterizedTypeReference<Map<String, String>>() {
  22. });
  23. Map<String, String> result = responseEntity.getBody();
  24. System.out.println(result);
  25. }

输出

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

4.6、发送json格式数据:传递java对象

接口

  1. /**
  2. * body中json格式的数据,返回值非泛型
  3. *
  4. * @param bookDto
  5. * @return
  6. */
  7. @PostMapping("/test/form4")
  8. @ResponseBody
  9. public BookDto form4(@RequestBody BookDto bookDto) {
  10. return bookDto;
  11. }

RestTemplate调用接口

  1. @Test
  2. public void test15() {
  3. RestTemplate restTemplate = new RestTemplate();
  4. String url = "http://localhost:8080/chat16/test/form4";
  5. BookDto body = new BookDto(1, "SpringMVC系列");
  6. BookDto result = restTemplate.postForObject(url, body, BookDto.class);
  7. System.out.println(result);
  8. }

输出

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

4.7、发送json格式数据:传递java对象,返回值为泛型

接口

  1. /**
  2. * body中json格式的数据,返回值为泛型
  3. *
  4. * @param bookDtoList
  5. * @return
  6. */
  7. @PostMapping("/test/form5")
  8. @ResponseBody
  9. public List<BookDto> form5(@RequestBody List<BookDto> bookDtoList) {
  10. return bookDtoList;
  11. }

用RestTemplate调用这个接口,代码如下

  1. @Test
  2. public void test16() {
  3. RestTemplate restTemplate = new RestTemplate();
  4. String url = "http://localhost:8080/chat16/test/form5";
  5. //①:请求体,发送的时候会被转换为json格式数据
  6. List<BookDto> body = Arrays.asList(
  7. new BookDto(1, "SpringMVC系列"),
  8. new BookDto(2, "MySQL系列"));
  9. //②:头
  10. HttpHeaders headers = new HttpHeaders();
  11. headers.add("header1", "v1");
  12. headers.add("header2", "v2");
  13. //③:请求实体
  14. RequestEntity requestEntity = new RequestEntity(body, headers, HttpMethod.POST, URI.create(url));
  15. //④:发送请求(请求实体,返回值需要转换的类型)
  16. ResponseEntity<List<BookDto>> responseEntity = restTemplate.exchange(
  17. requestEntity,
  18. new ParameterizedTypeReference<List<BookDto>>() {
  19. });
  20. //⑤:获取结果
  21. List<BookDto> result = responseEntity.getBody();
  22. System.out.println(result);
  23. }

输出

  1. [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,写法如下:

  1. @Test
  2. public void test17() {
  3. RestTemplate restTemplate = new RestTemplate();
  4. String url = "http://localhost:8080/chat16/test/form5";
  5. //①:请求体为一个json格式的字符串
  6. String body = "[{\"id\":1,\"name\":\"SpringMVC系列\"},{\"id\":2,\"name\":\"MySQL系列\"}]";
  7. /**
  8. * ②:若请求体为json字符串的时候,需要在头中设置Content-Type=application/json;
  9. * 若body是普通的java类的时候,无需指定这个,RestTemplate默认自动配上Content-Type=application/json
  10. */
  11. HttpHeaders headers = new HttpHeaders();
  12. headers.setContentType(MediaType.APPLICATION_JSON);
  13. //③:请求实体(body,头、请求方式,uri)
  14. RequestEntity requestEntity = new RequestEntity(body, headers, HttpMethod.POST, URI.create(url));
  15. //④:发送请求(请求实体,返回值需要转换的类型)
  16. ResponseEntity<List<BookDto>> responseEntity = restTemplate.exchange(
  17. requestEntity,
  18. new ParameterizedTypeReference<List<BookDto>>() {
  19. });
  20. //⑤:获取结果
  21. List<BookDto> result = responseEntity.getBody();
  22. System.out.println(result);
  23. }

输出

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

5、DELETE、PUT、OPTION请求

5.1、DELETE请求

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

5.2、PUT请求

PUT请求和POST请求类似,将类型改为PUT就可以了。

5.3、OPTIONS请求

OPTIONS请求用来探测接口支持哪些http方法

  1. public Set<HttpMethod> optionsForAllow(String url, Object... uriVariables);
  2. public Set<HttpMethod> optionsForAllow(String url, Map<String, ?> uriVariables);
  3. public Set<HttpMethod> optionsForAllow(URI url);

6、集成HttpClient

RestTemplate内部默认用的是jdk自带的HttpURLConnection发送请求的,性能上面并不是太突出。

可以将其替换为httpclient或者okhttp。

先来看下如何替换为HttpClient。

引入maven配置

  1. <dependency>
  2. <groupId>org.apache.httpcomponents</groupId>
  3. <artifactId>httpclient</artifactId>
  4. <version>4.5.7</version>
  5. </dependency>

创建RestTemplate时指定HttpClient配置,代码如下

  1. public HttpClient httpClient() {
  2. HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
  3. try {
  4. //设置信任ssl访问
  5. SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, (arg0, arg1) -> true).build();
  6. httpClientBuilder.setSSLContext(sslContext);
  7. HostnameVerifier hostnameVerifier = NoopHostnameVerifier.INSTANCE;
  8. SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslContext, hostnameVerifier);
  9. Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
  10. // 注册http和https请求
  11. .register("http", PlainConnectionSocketFactory.getSocketFactory())
  12. .register("https", sslConnectionSocketFactory).build();
  13. //使用Httpclient连接池的方式配置(推荐),同时支持netty,okHttp以及其他http框架
  14. PoolingHttpClientConnectionManager poolingHttpClientConnectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
  15. // 最大连接数
  16. poolingHttpClientConnectionManager.setMaxTotal(1000);
  17. // 同路由并发数
  18. poolingHttpClientConnectionManager.setDefaultMaxPerRoute(100);
  19. //配置连接池
  20. httpClientBuilder.setConnectionManager(poolingHttpClientConnectionManager);
  21. // 重试次数
  22. httpClientBuilder.setRetryHandler(new DefaultHttpRequestRetryHandler(0, true));
  23. //设置默认请求头
  24. List<Header> headers = new ArrayList<>();
  25. httpClientBuilder.setDefaultHeaders(headers);
  26. return httpClientBuilder.build();
  27. } catch (Exception e) {
  28. throw new RuntimeException(e);
  29. }
  30. }
  31. public ClientHttpRequestFactory clientHttpRequestFactory() {
  32. HttpComponentsClientHttpRequestFactory clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory(httpClient());
  33. // 连接超时(毫秒),这里设置10秒
  34. clientHttpRequestFactory.setConnectTimeout(10 * 1000);
  35. // 数据读取超时时间(毫秒),这里设置60秒
  36. clientHttpRequestFactory.setReadTimeout(60 * 1000);
  37. // 从连接池获取请求连接的超时时间(毫秒),不宜过长,必须设置,比如连接不够用时,时间过长将是灾难性的
  38. clientHttpRequestFactory.setConnectionRequestTimeout(10 * 1000);
  39. return clientHttpRequestFactory;
  40. }
  41. public RestTemplate restTemplate(){
  42. //创建RestTemplate的时候,指定ClientHttpRequestFactory
  43. return new RestTemplate(this.clientHttpRequestFactory());
  44. }
  45. @Test
  46. public void test18() {
  47. RestTemplate restTemplate = this.restTemplate();
  48. String url = "http://localhost:8080/chat16/test/get";
  49. //getForObject方法,获取响应体,将其转换为第二个参数指定的类型
  50. BookDto bookDto = restTemplate.getForObject(url, BookDto.class);
  51. System.out.println(bookDto);
  52. }

7、集成okhttp

引入maven配置

  1. <dependency>
  2. <groupId>com.squareup.okhttp3</groupId>
  3. <artifactId>okhttp</artifactId>
  4. <version>4.3.1</version>
  5. </dependency>

创建RestTemplate

  1. new RestTemplate(new OkHttp3ClientHttpRequestFactory());

8、总结

RestTemplate使用确实非常容易,建议大家去看一下RestTemplate的源码,debug跟踪一下过程,这样用起来就非常顺手了。

最新资料

更多内容请访问:IT源点

相关文章推荐

全部评论: 0

    我有话说: