Spring 框架中发送 http 请求 —— RestTemplate

时间:2024-10-21 16:08:35

对四年前的文章重新做了下修改

简介

这是由 Spring 提供的网络请求工具,它内嵌在 spring-web

    <dependency>
      <groupId></groupId>
      <artifactId>spring-web</artifactId>
      <version>5.3.12</version>
    </dependency>

当然我们常用的 spring-boot-starter-web 也是包含它的,所以就不需要像这里一样显式引用了,其他细节可参见 RestTemplate 官方文档

使用方法

注意事项

RestTemplate 默认行为,如果请求出去未返回 200 的 http 状态码,会抛出异常,我们通常希望抛出异常交由请求方自行决定,此时我们需要覆盖默认的 ResponseErrorHandler ,下面为用什么也不干覆盖默认处理机制

        RestTemplate restTemplate = new RestTemplate();
        ResponseErrorHandler responseErrorHandler = new ResponseErrorHandler() {
            @Override
            public boolean hasError(ClientHttpResponse clientHttpResponse) throws IOException {
                return true;
            }

            @Override
            public void handleError(ClientHttpResponse clientHttpResponse) throws IOException {

            }
        };
        (responseErrorHandler);

便捷 API

// 此处含义:向 url 发送 Get 类型请求,将请求返回的结果反序列化成 String 类型
(url, );

其他 postForObject 等类似换汤不换药 API 也是如此,通常我们不去使用,它们不够*,无法设置编码格式等,乱码等问题无法解决,不再赘述,后面只将通用方案

通用方案

() 它包含一个 HttpMethod 的入参,该参数决定了我们的请求是 GetPost …… 中的哪一种

画外音: 顺便提一嘴原生的 RestTemplate 其实并不支持 Patch 类型的请求,可以引入第三方实现来扩展 RestTemplate 功能

示例

application/json 请求

        RestTemplate restTemplate = new RestTemplate();
        String url = "";
        /* 注意:必须 http、https……开头,不然报错,浏览器地址栏不加 http 之类不出错是因为浏览器自动帮你补全了 */
        HttpHeaders headers = new HttpHeaders();
        /* 这个对象有add()方法,可往请求头存入信息 */
        (MediaType.APPLICATION_JSON_UTF8);
        /* 解决中文乱码的关键 , 还有更深层次的问题 关系到 StringHttpMessageConverter,先占位,以后补全*/
        HttpEntity<String> entity = new HttpEntity<String>(body, headers);
        /* body是Http消息体例如json串 */
        (url, , entity, );
        /*上面这句返回的是往 url发送 post请求 请求携带信息为entity时返回的结果信息  是可以修改的,其实本质上就是在指定反序列化对象类型,这取决于你要怎么解析请求返回的参数*/

application/x-www-form-urlencoded 请求

        RestTemplate restTemplate=new RestTemplate();
        /* 注意:必须 http、https……开头,不然报错,浏览器地址栏不加 http 之类不出错是因为浏览器自动帮你补全了 */
        String url="";
        String bodyValTemplate = "var1=" + ("测试数据1", "utf-8") + "&var2=" + ("test var2", "utf-8");
        HttpHeaders headers = new HttpHeaders();
        (MediaType.APPLICATION_FORM_URLENCODED);
        HttpEntity entity = new HttpEntity(bodyValTemplate, headers);
        (url, , entity, );

另外此处 HttpEntity entity 构造过程替换成下面方式也是等效的

        LinkedMultiValueMap body=new LinkedMultiValueMap();
        ("var1","测试数据1");
        ("var2","test Val2");
        HttpEntity entity = new HttpEntity(body, headers);

等效于 Postman 这样的请求

传输文件 请求

    @PostMapping("/file2")
    public ResponseEntity<?> fileUpload2(@RequestParam("files") ArrayList<MultipartFile> filesList) throws IOException {
        MultiValueMap<String, Object> requestBody = new LinkedMultiValueMap<>();
        for (MultipartFile multipartFile : filesList) {
            Resource resource = new InputStreamResource(()) {
                @Override
                public String getFilename() {
                    return ();
                }

                @Override
                public long contentLength() {
                    return ();
                }
            };
            ("files", resource);
        }
        HttpHeaders httpHeaders = new HttpHeaders();
        (HttpHeaders.CONTENT_TYPE, MediaType.MULTIPART_FORM_DATA_VALUE);
        HttpEntity<MultiValueMap<String, ?>> requestEntity = new HttpEntity<>(requestBody, httpHeaders);
        ResponseEntity<String> responseEntity = ("http://xxxxxx/file", , requestEntity, );
        return success(());
    }

这是一个 Controller 层通过 RestTemplate 转发文件的示例,至于这段代码是怎么来的,请参见 官方文档 Multipart 的描述 和底部参考资料

简而言之,RestTemplate 原生通过 FormHttpMessageConverter 来支持 multipart/form-data 请求,这个过程中它认 Resource 对象作为请求的内容

FileSystemResource 符合条件,但需要一个真实文件存在于本地或远端,理想状态最好就在内存中存在,执行结束就丢弃,不在本地产生其他负作用,求其次就使用了 InputStreamResource 对象

接着追踪 FormHttpMessageConverter 代码你会发现,它依赖了 ResourcegetFilename()contentLength()方法,前者如果为 null 会忽略该文件的传输;后者则是因为默认实现是会去读流,而流只能读一次,这一次机会在计算文件长度上了就得不偿失

异步操作

AsyncRestTemplate 是用来进行异步网络请求操作的,但是在后续版本官方放弃了这个方案,推荐你使用 WebClient,这条路子可以说是寄了(4 年前我还天真的以为自己会来补那部分内容,毕竟当时 AsyncRestTemplate 没有被标记 Deprecated)

参考资料

通过RestTemplate上传文件(InputStreamResource详解)

HttpClient与CloseableHttpClient