前言
在某种情况下,后台服务可能需要访问另一台服务器的REST接口。以前估计不少人用的都是HttpRequest类来着,结合Paser解析JSON格式的Body。现在Spring Boot的Web Starter就自带了RestTemplate,直接用它的就好了。最好不要再往项目里导新的依赖。这里做了点整理,分享出来。发
简单的请求
(1)GET请求
案例如下:
RestTemplate restTemplate = new RestTemplate(); ResponseEntity<CustomClass> responseEntity = restTemplate.getForEntity(url, CustomClass.class);
CustomClass response = responseEntity.getBody();
- getForEntity 顾名思义就是发送GET请求,并得到一个对象
- 这里的URL应该是完整的,即应该是这样的格式:"http://域名:端口/xxx/yyy"
- CustomClass是你自己定义的类,tempalte会将请求响应的JSON格式的body解析成CustomClass。
- ResponseEntity是请求响应的结果,可以获取响应码,以及最重要的响应Body
注意:由于RestTemplate是使用Jackson来进行映射的,而Jackson本质上是用setter/getter来实现的。所以你定义的类必须有setter。其中JSON字符串转对象需要setter,对象转JSON字符串需要getter。
(2)POST请求
案例如下:
... restTemplate.postForEntity(url, ParamObject, Response.class); ...
- 基本同上,不过postForEntity的第二个参数是请求对象,底层会将其转为JSON字符存入请求Body中
传输form/data
(1)普通的表单请求
MultiValueMap<String, Object> param = new LinkedMultiValueMap<>(); param.add("param1","1232"); param.add("param2","12312"); HttpEntity<MultiValueMap<String,Object>> httpEntity = new HttpEntity<>(param); ResponseEntity<Response> responseEntity = restTemplate.postForEntity(url, httpEntity, Response.class);
这就相对于提交了一个普通的表单
(2)带文件的表单请求——文件在本地
MultiValueMap<String, Object> param = new LinkedMultiValueMap<>(); param.add("file", new FileSystemResource(new File(filePath)));
...
- 页面上选择文件,上传的请求一般就叫file
- 经过FileSystemResource包装后,RestTemplate会利用InputStream从磁盘中读入文件内容,并写入HTTP请求body中。
(3)带文件的表单请求——文件从客户端而来(MultipartFile)
File file = File.createTempFile("tmp", null); multipartFile.transferTo(file); file.deleteOnExit(); MultiValueMap<String, Object> param = new LinkedMultiValueMap<>(); param.add("file", new FileSystemResource(file)); ...
可以将客户端上传的内容暂存于本地,然后再发送给另一个服务器。(注:这种方法不太好,需要访问磁盘,可以参照下例的实现)
(4)带文件的表单请求——本地没有文件,只有文件的文本内容
可能存在这种情况,即你本地保存的是文本文件的内容,(如保存在数据库中,用text存储。因为这样比较方便管理)而另一个服务器需要以文件的形式接收你的文本内容。即它会用一个MultipartFile来接收。
这时候,你不可能把数据写到文件中,然后再发送文件。这种操作是非常愚蠢的。既然我们已经知道请求本质上传递的是字节数组,那何不就仿造FileSystemResource写一个适配类呢?
public class TextFeignECLFileResource extends AbstractResource { private byte[] bytes; private int index = 0; private String filename; public TextFeignECLFileResource(String filename, String content) { this.filename = filename; this.bytes = content.getBytes(); } @Override public String getDescription() { return null; } @Override public InputStream getInputStream() throws IOException { return new InputStream() { @Override public int read() throws IOException { //注意这里的返回值int表示的是字节 if (index >= bytes.length) return -1; return bytes[index++]; } }; } @Override public boolean exists() { return true; } @Override public boolean isFile() { return true; } @Override public boolean isReadable() { return true; } @Override public long contentLength() throws IOException { return bytes.length; } @Override public String getFilename() { return filename; } }
使用方式:
MultiValueMap<String, Object> param = new LinkedMultiValueMap<>(); param.add("file", new TextFeignECLFileResource("test.txt", "hello world"));
注意:这里之所以能这么做是因为普通的文本文件没有文件头等额外的描述信息