当前位置: 首页 > news >正文

RestTemplate使用详解

文章目录

  • 简介
  • 基本操作
  • uri参数
  • 传递json参数与header参数设置
  • form-data
  • exchange复杂类型处理
  • 上传文件
  • 下载文件

简介

对于http请求之前一直用apache的httpclient,已经习惯了,特别是使用fluent之后,更加方便了。

所以一直没有怎么太过关注RestTemplate,最近才发现在Spring环境中使用RestTemplate还是很方便。

特别是处理带有多重泛型的复杂类型时,httpclient需要一层一层的自己处理,RestTemplate只需要指定类型参数ParameterizedTypeReference就能完美解决。

RestTemplate默认的参数和文件名编码也很方便,基本不用额外处理,就不会有乱码问题。

另外,RestTemplate默认配置就能解决绝大部分场景,所以使用直接new就可以,没有过多配置。

所以,当你下一次在Spring环境中需要使用http请求时,可以考虑一下使用RestTemplate。

基本操作

  1. xxxForObject与xxxForEntity的区别:xxxForObject得到的结果是HTTP返回的body部分,xxxForEntity是整个HTTP返回部分,包含状态码等信息
  2. getForXXX与postForXXX:get请求没有body部分,post请求可以设置body部分
  3. responseType指定的是返回body部分的数据类型,对于复杂类型还需要指定泛型参数

服务端接口:

import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;@RestController
@RequestMapping("/rt-template")
public class RestTemplateController {@RequestMapping("/base")public String base() {return "base执行";}@PostMapping("/param")public String param(@RequestParam("id") Long id,@RequestParam("name") String name) {System.out.println("获取到参数id:" + id + " name:" + name);return id + "_" + name;}
}

RestTemplate请求:

 @Test
public void base() {RestTemplate restTemplate = new RestTemplate();String url = "http://localhost:8087/rt-template/base";String result = restTemplate.getForObject(url, String.class);System.out.println(result);result = restTemplate.postForObject(url, null, String.class);System.out.println(result);ResponseEntity<String> entity = restTemplate.getForEntity(url, String.class);System.out.println(entity.getStatusCode() + " " + entity.getBody());entity = restTemplate.postForEntity(url, null, String.class);System.out.println(entity.getStatusCode() + " " + entity.getBody());
}

uri参数

RestTemplate有个map参数可以设置url的参数

例如:http://localhost:8087/rt-template/base?id=123&name=tim

id和name参数可以通过占位符设置,RestTemplate提供了2种方式:

  1. map,根据名字替换
  2. 可变参数object,根据顺序替换

url参数主要是针对get请求的传参,不过在RestTemplate中post请求也可以使用该方式。

@Test
public void paramMap() {RestTemplate restTemplate = new RestTemplate();String url = "http://localhost:8087/rt-template/base?id={id}&name={name}";Map<String, Object> uriVariables = new HashMap<>();uriVariables.put("id", "123456");uriVariables.put("name", "tim");String result = restTemplate.getForObject(url, String.class, uriVariables);
//        String result = restTemplate.postForObject(url, null,String.class, uriVariables);System.out.println(result);
}@Test
public void paramObject() {RestTemplate restTemplate = new RestTemplate();String url = "http://localhost:8087/rt-template/base?id={id}&name={name}";String result = restTemplate.getForObject(url, String.class, 123456, "tim");
//        String result = restTemplate.postForObject(url, null, String.class, 123456, "tim");System.out.println(result);
}

传递json参数与header参数设置

在接口中我们使用最多的还是json参数,例如后端使用@RequestBody。

@RequestMapping("/request-body")
public String requestBody(@RequestBody UserTO user){System.out.println(user);return user.toString();
}
@Test
public void postJson() {String url = "http://localhost:8087/rt-template/request-body";RestTemplate restTemplate = new RestTemplate();HttpHeaders headers = new HttpHeaders();headers.setContentType(MediaType.APPLICATION_JSON);String json = "{\"name\":\"tim\"}";HttpEntity<String> request = new HttpEntity<>(json, headers);restTemplate.postForEntity(url, request, String.class);
}

对于@RequestBody有几点需要说明:

  1. 请求必须是post的json数据
  2. @RequestBody可以和@RequestParam一起使用,请求使用post,body为json,@RequestParam获取uri中的参数

@RequestBody从参数可以非常灵活,只要请求是post的json都能正常解析:

  1. @RequestBody String uid
  2. @RequestBody String[] ids
  3. @RequestBody List<String> ids
  4. List<Map<String,Object>> list
  5. @RequestBody User user
  6. @RequestBody List<User> userList

对于 @RequestBody请求不是json时,最常见的错误是:
Content-Type ‘application/x-www-form-urlencoded;charset=UTF-8’ is not supported

基本可以确定是请求端使用了默认的Content-Type,而不是json。

form-data

RestTemplate的post可以直接设置一个map来传递参数

@Test
public void formData() {String url = "http://localhost:8087/rt-template/request-body";RestTemplate restTemplate = new RestTemplate();MultiValueMap<String, Object> paramMap = new LinkedMultiValueMap<>();paramMap.add("name", "tim");String result = restTemplate.postForObject(url, paramMap, String.class);System.out.println(result);
}

上面这个请求默认的Content-Type 是’application/x-www-form-urlencoded;charset=UTF-8’

在服务端,可以使用@RequestParam(“name”),或者直接用一个对象来获取参数:

@RequestMapping("/param")
public String param(@RequestParam("name") String name) {System.out.println("获取到参数name:" + name);return name;
}@RequestMapping("/form-data")
public String normalUser(UserTO user){System.out.println(user);return user.toString();
}

当然,也可以把参数封装在HttpEntity中:

@Test
public void formDataEntity() {String url = "http://localhost:8087/rt-template/form-data";RestTemplate restTemplate = new RestTemplate();MultiValueMap<String, Object> paramMap = new LinkedMultiValueMap<>();paramMap.add("name", "tim");HttpHeaders headers = new HttpHeaders();headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);HttpEntity<MultiValueMap<String, Object>> httpEntity = new HttpEntity<>(paramMap, headers);String user = restTemplate.postForObject(url, httpEntity, String.class);System.out.println(user);
}

exchange复杂类型处理

其实,前面介绍的postxxx、getxxx、xxxForObject、xxxForEntity最终都是调用了exchange。

我们当然也可以直接调用exchange方法,不过我们很少直接调用,因为直接使用exchange稍微复杂一点。

我们什么时候需要exchange呢?

最常见的就是处理复杂类型泛型的时候,因为exchange行有一个ParameterizedTypeReference responseType参数。

前面我们已经感受到了RestTemplate最方便的地方就是能自动帮我们处理返回结果,包装为类。

当处理复杂类型的时候就需要ParameterizedTypeReference。

例如,像下面这个返回Result<List>:

@RequestMapping("/get-users")
public Result<List<UserTO>> getUsers(){LinkedList<UserTO> list = new LinkedList<>();list.add(UserTO.builder().id(1).name("tim").age(18).build());list.add(UserTO.builder().id(2).name("allen").age(20).build());return getSuccess(list);
}public static <T> Result<T> getSuccess(T data){return Result.<T>builder().success(true).data(data).message("执行成功").build();
}
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class UserTO {private Integer id;private String name;private Integer age;private Date birthday;}
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class Result <T> {/*** 编码区分消息类型*/private Integer code;/*** 是否执行成功*/private Boolean success;/*** 执行结果消息*/private String message;/*** 数据*/private T data;
}

该我们ParameterizedTypeReference登场了

@Test
public void getUsers() {RestTemplate restTemplate = new RestTemplate();String url = "http://localhost:8087/rt-template/get-users";ParameterizedTypeReference<Result<List<UserTO>>> type = new ParameterizedTypeReference<>() {};HttpHeaders headers = new HttpHeaders();HttpEntity<?> httpEntity = new HttpEntity<>(headers);ResponseEntity<Result<List<UserTO>>> entity = restTemplate.exchange(url, HttpMethod.GET, httpEntity, type);Result<List<UserTO>> result = entity.getBody();System.out.println(result);
}

重点:ParameterizedTypeReference<Result<List>> type = new ParameterizedTypeReference<>() {};

只需要把我们返回参数的泛型具化就可以了。

上传文件

@PostMapping("/upload")
public Result<?> upload(@RequestParam("file") MultipartFile uploadFile,@RequestParam("id") Long id) {String oldName = uploadFile.getOriginalFilename();try {Path dest = Paths.get("F:\\pic", String.valueOf(id), oldName);File file = dest.toFile();if (!file.exists()) {File parentFile = file.getParentFile();if (!parentFile.exists()) {if (!parentFile.mkdirs()) {ResultHelper.getFailResult("创建目录失败");}}if (!file.createNewFile()) {ResultHelper.getFailResult("创建文件失败");}}uploadFile.transferTo(dest);} catch (Exception e) {log.error(e.getMessage(), e);ResultHelper.getFailResult("上传文件失败");}return ResultHelper.getDefaultSuccess();
}

最重要的是设置content type为MULTIPART_FORM_DATA

@Test
public void upload() {String filePath = "F:\\index.html";String url = "http://localhost:8087/rt-template/upload";HttpHeaders headers = new HttpHeaders();// multipart/form-dataheaders.setContentType(MediaType.MULTIPART_FORM_DATA);headers.addIfAbsent("token","123");MultiValueMap<String, Object> param = new LinkedMultiValueMap<>();param.add("file", new FileSystemResource(new File(filePath)));param.add("id", "123");HttpEntity<MultiValueMap<String, Object>> request = new HttpEntity<>(param, headers);RestTemplate restTemplate = new RestTemplate();Result<?> result = restTemplate.postForObject(url, request, Result.class);System.out.println(result);
}

下载文件

@GetMapping("/download/{id}/{name}")
public void download(@PathVariable(value = "id") Long id,@PathVariable(value = "name") String name,HttpServletRequest request,HttpServletResponse response) throws Exception {Path path = Paths.get("F:\\pic", String.valueOf(id), name);File file = path.toFile();if (file.exists()) {FileInputStream fis = new FileInputStream(file);response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);response.setHeader("content-disposition","attachment;fileName=" + URLEncoder.encode(name, StandardCharsets.UTF_8));OutputStream os = response.getOutputStream();FileCopyUtils.copy(fis, os);}
}

指定body类型为byte数组就可以:

@Test
public void downloadFile() throws IOException {Long id = 123L;String fileName = "index.html";String url = "http://localhost:8087/rt-template/download/{1}/{2}";ResponseEntity<byte[]> entity = restTemplate.getForEntity(url, byte[].class, id, fileName);if (entity.getStatusCode().is2xxSuccessful()) {byte[] body = entity.getBody();if (body == null || body.length == 0) {throw new RuntimeException("文件内容为空");}FileCopyUtils.copy(body, new FileOutputStream("F:\\tmp\\" + fileName));} else {System.out.println("请求异常:" + entity.getStatusCode());}
}

相关文章:

  • 二叉树的顺序实现-堆
  • SwiftUI 5.0(iOS 17)进一步定制 TipKit 外观让撸码如虎添翼
  • Android UI控件详细解析(四)
  • 【新能源大巴BMS结构与乘用车的区别】
  • 每日一题——Python实现PAT甲级1041 Be Unique(举一反三+思想解读+逐步优化)
  • java使用资源过高排查
  • 解析Java中1000个常用类:Cloneable类,你学会了吗?
  • linux-gpio
  • 【代码随想录算法训练营第37期 day21 | LeetCode530.二叉搜索树的最小绝对差、501.二叉搜索树中的众数、236. 二叉树的最近公共祖先】
  • Java集合【超详细】
  • 实战经验分享之移动云快速部署Stable Diffusion SDXL 1.0
  • K8S中Prometheus+Grafana监控
  • Wpf 使用 Prism 实战开发Day30
  • YOLOv5训练自定义数据集模型的参数与指令说明
  • Flutter 中的 SliverFillRemaining 小部件:全面指南
  • [译] 理解数组在 PHP 内部的实现(给PHP开发者的PHP源码-第四部分)
  • 【399天】跃迁之路——程序员高效学习方法论探索系列(实验阶段156-2018.03.11)...
  • 【面试系列】之二:关于js原型
  • 07.Android之多媒体问题
  • 8年软件测试工程师感悟——写给还在迷茫中的朋友
  • AHK 中 = 和 == 等比较运算符的用法
  • canvas实际项目操作,包含:线条,圆形,扇形,图片绘制,图片圆角遮罩,矩形,弧形文字...
  • classpath对获取配置文件的影响
  • HashMap ConcurrentHashMap
  • Java深入 - 深入理解Java集合
  • JAVA之继承和多态
  • 阿里云前端周刊 - 第 26 期
  • 二维平面内的碰撞检测【一】
  • 分布式事物理论与实践
  • 浮现式设计
  • 关于extract.autodesk.io的一些说明
  • 关于Java中分层中遇到的一些问题
  • 技术发展面试
  • 开年巨制!千人千面回放技术让你“看到”Flutter用户侧问题
  • 配置 PM2 实现代码自动发布
  • 前端面试题总结
  • 如何使用 JavaScript 解析 URL
  • 使用agvtool更改app version/build
  • 想写好前端,先练好内功
  • 用Canvas画一棵二叉树
  • 掌握面试——弹出框的实现(一道题中包含布局/js设计模式)
  • 阿里云ACE认证之理解CDN技术
  • ​一文看懂数据清洗:缺失值、异常值和重复值的处理
  • ‌前端列表展示1000条大量数据时,后端通常需要进行一定的处理。‌
  • ‌移动管家手机智能控制汽车系统
  • #if #elif #endif
  • (1)svelte 教程:hello world
  • (二)pulsar安装在独立的docker中,python测试
  • (附源码)ssm基于web技术的医务志愿者管理系统 毕业设计 100910
  • (三分钟)速览传统边缘检测算子
  • (四)图像的%2线性拉伸
  • (算法)区间调度问题
  • (转)scrum常见工具列表
  • (转)微软牛津计划介绍——屌爆了的自然数据处理解决方案(人脸/语音识别,计算机视觉与语言理解)...
  • *算法训练(leetcode)第四十七天 | 并查集理论基础、107. 寻找存在的路径