RestTemplate使用手册

传统服务端请求HTTP服务,一般使用Jdk自带HttpURLConnection 或者Apache工具类HttpClient,请求过于繁琐与复杂,还需要关注资源回收。

因此,Spring 提供RestTemplate 模板类简化操作。RestTemplate 属于HTTP请求同步阻塞式工具类,底层基于HTTP客户端库(例如JDK HttpURLConnection 、Apache HttpComponents 或者okHttp等)封装更加简单易用模板方法API,方便程序员利用模板方法发起网络请求和处理,很大程度上能够提升开发效率。

01 使用案例

1.1 非Spring项目

java 复制代码
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-web</artifactId>
  <version>5.2.6.RELEASE</version>
</dependency>
java 复制代码
public class RestTemplateMain {
    public static void main(String[] args) {
        RestTemplate restTemplate = new RestTemplate();
        String url = "http://localhost:8080/youxinren/user";
        String reponse = restTemplate.getForObject(url, String.class);
        System.out.println(reponse);
    }
}

1.2 Spring项目

gradle 复制代码
implementation platform('org.springframework.boot:spring-boot-dependencies:2.3.10.RELEASE')
implementation group: 'org.springframework.boot', name: 'spring-boot-starter-web'
java 复制代码
@Configuration
public class RestTemplateConfig {
    @Bean
    @ConditionalOnMissingBean(RestTemplate.class)
    public RestTemplate restTemplate(){
        RestTemplate restTemplate = new RestTemplate();
        return restTemplate;
    }
}

注意,RestTemplate 默认使用JDK自带HttpURLConnection 作为底层客户端实现,不过也支持切换至高性能OkHttp 或者Apache的HttpClient 客户端。从性能方面对比,OkHttp 工具类性能明显高于HttpClient ,然而HttpClient 又高于HttpURLConnection

java 复制代码
@Configuration
public class RestTemplateConfig {
    @Bean
    @ConditionalOnMissingBean(RestTemplate.class)
    public RestTemplate restTemplate(){
        RestTemplate restTemplate = new RestTemplate(getClientHttpRequestFactory());
        return restTemplate;
    }

    // 使用HttpClient作为底层客户端
    private ClientHttpRequestFactory getClientHttpRequestFactory() {
        int timeout = 5000;
        RequestConfig config = RequestConfig.custom()
                .setConnectTimeout(timeout)
                .setConnectionRequestTimeout(timeout)
                .setSocketTimeout(timeout)
                .build();
        CloseableHttpClient client = HttpClientBuilder
                .create()
                .setDefaultRequestConfig(config)
                .build();
        return new HttpComponentsClientHttpRequestFactory(client);
    }
}
java 复制代码
@Configuration
public class RestTemplateConfig {
    @Bean
    @ConditionalOnMissingBean(RestTemplate.class)
    public RestTemplate restTemplate(){
        RestTemplate restTemplate = new RestTemplate(getClientHttpRequestFactory());
        return restTemplate;
    }

    // 使用OkHttpClient作为底层客户端
    private ClientHttpRequestFactory getClientHttpRequestFactory() {
        OkHttpClient okHttpClient = new OkHttpClient.Builder()
                .connectTimeout(5, TimeUnit.SECONDS)
                .writeTimeout(5, TimeUnit.SECONDS)
                .readTimeout(5, TimeUnit.SECONDS)
                .build();
        return new OkHttp3ClientHttpRequestFactory(okHttpClient);
    }
}

1.3 拦截器

使用RestTemplate调用远程服务,可以使用拦截器添加请求头传递信息,比如添加TraceId便于日志串联完整请求链路,提升问题定位速读。

第一步,定义LogFilter拦截所有接口请求,使用MDC底层ThreadLocal变量保存请求TraceId。

java 复制代码
public class LogFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        if (StringUtils.isBlank(MDC.get("TRACE_ID"))) {
            MDC.put(""TRACE_ID"", UUID.randomUUID().toString());
        }
        chain.doFilter(request, response);
    }
}

第二步,实现ClientHttpRequestInterceptor接口,通过MDC获取当前请求TraceId,然后设置到请求头。

java 复制代码
public class RestTemplateInterceptor implements ClientHttpRequestInterceptor {
    @Override
    public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
        request.getHeaders().set("traceId", MDC.get("TRACE_ID"));
        return execution.execute(request, body);
    }
}

第三步,配置RestTemplate自定义RestTemplateInterceptor实现。

java 复制代码
@Configuration
public class RestTemplateConfiguration {
    @Bean
    public RestTemplate restTemplate() {
        RestTemplate restTemplate = new RestTemplate();
        restTemplate.setInterceptors(Collections.singletonList(restTemplateInterceptor()));
        return restTemplate;
    }

    @Bean
    public RestTemplateInterceptor restTemplateInterceptor() {
        return new RestTemplateInterceptor();
    }
}

1.4 扩展请求类型

SpringBoot使用RestTemplate远程调用接口,不支持Content-Type=text/html;charset=UTF-8类型,导致出现no suitable HttpMessageConverter异常。

java 复制代码
@Configuration
public class RestTemplateConfiguration {
    @Bean
    public RestTemplate restTemplate() {
        RestTemplate restTemplate = new RestTemplate();
        MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
        converter.setSupportedMediaTypes(Arrays.asList(MediaType.TEXT_HTML, MediaType.TEXT_PLAIN));
        restTemplate.getMessageConverters().add(converter);

        return restTemplate;
    }
}

02 RestTemplate API

RestTemplate 包装各种网络请求方式(GETPOSTPUTDELETE和文件上传与下载),极大简化开发工作量。

2.1 GET

RestTemplate提供两种方式发送GET请求,getForObject仅返回响应体,getForEntity返回ResponseEntity封装响应体、请求头信息。

java 复制代码
public class TestRestTemplate {
    @Autowired
    private RestTemplate restTemplate;

    // 不带参get请求
    @Test
    public void testGetNoArgument(){
        String url = "http://host:8080/getNoArgument";
        // 发起请求, 直接返回对象
        ResponseBean responseBean = restTemplate.getForObject(url, ResponseBean.class);
        System.out.println(responseBean);
    }

    // 使用占位符号传参
    @Test
    public void testGetWithArgument(){
        // 使用占位符号传参
        String url = "http://host:8080/getWithArgument/{1}";

        // 发起请求, 直接返回对象(Restful风格)
        ResponseBean responseBean = restTemplate.getForObject(url, ResponseBean.class, "001");
        System.out.println(responseBean);
    }

    // 带参请求
    @Test
    public void testGetForObject1(){
        String url = "http://host:8080/getForObject?userName={userName}&password={password}";

        // 请求参数
        Map<String, String> uriVariables = new HashMap<>();
        uriVariables.put("userName", "feiyu");
        uriVariables.put("password", "123456");

        // 发起请求, 直接返回对象(带参数请求)
        ResponseBean responseBean = restTemplate.getForObject(url, ResponseBean.class, uriVariables);
        System.out.println(responseBean);
    }
    
    @Test
    public void testGetForObject2() {
        // URL特殊符号进行转义查询, 如{、}
        String param = "{"lat":" + latitude + ","lon":" + longitude + ","ver": 1}";
        URI requestUrl = UriComponentsBuilder.fromHttpUrl(url)
                .queryParam("param", param)
                .build().encode().toUri();
        JSONObject result = restTemplate.getForObject(requestUrl, JSONObject.class);
    }
}

RestTemplate工具类还提供exchange通用请求方法,用于封装GET、POST、DELETE、PUT、OPTIONS、PATCH等方法请求。

java 复制代码
public class TestRestTemplate {
    @Autowired
    private RestTemplate restTemplate;

    @Test
    public void testGetForEntity(){
        String url = "http://localhost:8080/getForEntity";

        // 发起请求, 返回全部信息
        ResponseEntity<ResponseBean> response = restTemplate.getForEntity(url, ResponseBean.class);

        // 获取响应体
        System.out.println("响应体=" + response.getBody().toString());

        // 以下是getForEntity比getForObject多出来的内容
        HttpStatus statusCode = response.getStatusCode();
        int statusCodeValue = response.getStatusCodeValue();
        HttpHeaders headers = response.getHeaders();

        System.out.println("响应状态=" + statusCode);
        System.out.println("响应状态码=" + statusCodeValue);
        System.out.println("响应头信息=" + headers);
    }

    @Test
    public void testExchange() {
        String url = "http://localhost:8080/exchange";

        // 请求头
        HttpHeaders headers = new HttpHeaders();
        headers.add("auth", "xuhx");

        // 封装请求头
        HttpEntity<MultiValueMap<String, Object>> formEntity = new HttpEntity<>(headers);
        ResponseEntity<Map<?, ?>> exchange = restTemplate.exchange(url, HttpMethod.GET, formEntity, Map.class);
    }
}

2.2 POST

POST请求用于向服务器提交数据,属于非幂等操作。与GET请求不同,POST封装请求数据到请求体,而不是URL,适用于传输大量数据和敏感信息。

java 复制代码
class TestRestTemplate {
    @Autowired
    private RestTemplate restTemplate;

    // 模拟表单提交
    @Test
    public void testPostByForm() {
        String url = "http://host:8080/testPostByForm";

        // 请求头: Content-Type=x-www-form-urlencoded
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);

        // 提交参数设置
        MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
        map.add("userName", "feiyu");
        map.add("password", "123456");

        // 组装请求体
        HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(map, headers);

        // 发起请求
        ResponseBean responseBean = restTemplate.postForObject(url, request, ResponseBean.class);
        System.out.println(responseBean.toString());
    }

    // 模拟JSON提交
    @Test
    public void testPostByJson() {
        String url = "http://host:8080/testPostByJson";

        // 入参
        RequestBean request = new RequestBean();
        request.setUserName("feiyu");
        request.setPassword("123456789");

        ResponseBean responseBean = restTemplate.postForObject(url, request, ResponseBean.class);
        System.out.println(responseBean.toString());
    }

    // 模拟重定向
    @Test
    public void testPostByLocation(){
        String url = "http://host:8080/testPostByLocation";

        // 入参
        RequestBean request = new RequestBean();
        request.setUserName("feiyu");
        request.setPaaword("123456789");

        // 接收服务器重定向URL, 服务器返回值=redirect:index.html
        // 打印: http://host:8080/index.html
        URI uri = restTemplate.postForLocation(url, request);
        System.out.println(uri.toString());
    }
}

2.3 PUT

HTTP PUT请求用于创建新资源或者替换目标资源。其次,PUT请求属于幂等操作,意味着无论调用多次效果都相同,不会产生副作用。

java 复制代码
public class TestRestTemplate {
    @Autowired
    private RestTemplate restTemplate;
    
    // 模拟PUT请求JSON提交
    @Test
    public void testPutByJson(){
        String url = "http://host:8080/testPutByJson";
        
        // 入参
        RequestBean request = new RequestBean();
        request.setUserName("feiyu");
        request.setPassword("123456789");

        // 模拟PUT请求JSON提交, 没有返回值
        restTemplate.put(url, request);
    }
}

2.4 DELETE

HTTP DELETE请求用于删除指定资源,广泛应用于数据库或者文件系统交互操作,例如删除用户、删除文件信息。

java 复制代码
class TestRestTemplate {
    @Autowired
    private RestTemplate restTemplate;
    
    // 模拟JSON提交, DELETE请求
    @Test
    public void testDeleteByJson(){
        String url = "http://localhost:8080/testDeleteByJson";

        // 提交DELETE请求, 没有返回值
        restTemplate.delete(url);
    }
}

2.5 文件上传与下载

大文件下载需要设置请求头ContentType=APPLICATION_OCTET_STREAM,表示以流的形式进行数据加载。RequestCallback结合File.copy保证接收到一部分文件内容,就向磁盘写入一部分内容,而不是全部加载到内存,最后写入磁盘文件。

java 复制代码
public class TestRestTemplate {
    @Autowired
    private RestTemplate restTemplate;

    // POST请求模拟文件上传
    @Test
    public void testUpload(){
        String url = "http://host:8080/upload";

        // 请求头: ContentType=multipart/form-data
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.MULTIPART_FORM_DATA);

        // 提交参数设置
        MultiValueMap<String, Object> paramMap = new LinkedMultiValueMap<>();
        paramMap.add("file", new FileSystemResource(new File("image1.jpg")));
        paramMap.add("file", new FileSystemResource(new File("image2.jpg")));
        // 服务端接受额外参数, 可以传递
        paramMap.add("userName", "feiyu");

        // 组装请求体
        HttpEntity<MultiValueMap<String, Object>> request = new HttpEntity<>(paramMap, headers);

        // 发起请求
        ResponseBean responseBean = restTemplate.postForObject(url, request, ResponseBean.class);
        System.out.println(responseBean.toString());
    }

    // POST请求模拟文件下载
    @Test
    public void downloadFile() throws IOException {
        String url = "http://host:8080/downloadFile/{1}/{2}";

        // 发起请求, 直接返回对象(restful风格)
        ResponseEntity<byte[]> rsp = restTemplate.getForEntity(url, byte[].class, userName,fileName);
        System.out.println("文件下载请求结果状态码 = " + rsp.getStatusCode());

        // 下载文件内容保存到本地
        Files.write(Paths.get("image.jpg"), Objects.requireNonNull(rsp.getBody(), "未获取到下载文件"));
    }

    // 大文件下载
    @Test
    public void downloadBigFile() throws IOException {
        String url = "http://host:8080/downloadFile/{1}";

        // 定义请求头接收类型
        RequestCallback requestCallback = request -> request.getHeaders()
                .setAccept(Arrays.asList(MediaType.APPLICATION_OCTET_STREAM, MediaType.ALL));

        // 响应进行流式处理而不是全部加载到内存
        String targetPath = "data.csv";
        restTemplate.execute(url, HttpMethod.GET, requestCallback, response -> {
            Files.copy(response.getBody(), Paths.get(targetPath));
            return null;
        }, userName);
    }
}

03 总结

RestTemplate通过高度封装请求API、深度整合Spring生态以及灵活扩展机制,显著降低HTTP通信开发复杂度。尽管Spring5推荐使用WebClient作为响应式编程替代方案,但是RestTemplate在传统同步场景仍具有广泛应用价值。

相关推荐
孤狼程序员1 天前
【Spring Cloud微服务】7.拆解分布式事务与CAP理论:从理论到实践,打造数据一致性堡垒
spring·spring cloud·微服务
zkmall1 天前
电商高并发稳赢指南:ZKmall开源商城微服务架构的实战拆解
微服务·架构·开源
橙子小哥的代码世界1 天前
【Docker】2025版Ubuntu 22.04 安装 Docker & Docker Compose 指南
linux·ubuntu·docker·微服务·容器编排·docker engine·docker compose
过期动态1 天前
MySQL内置的各种单行函数
java·数据库·spring boot·mysql·spring cloud·tomcat
齐 飞1 天前
XXL-JOB快速入门
spring boot·后端·spring cloud
AAA修煤气灶刘哥1 天前
从 “库存飞了” 到 “事务稳了”:后端 er 必通的分布式事务 & Seata 闯关指南
java·后端·spring cloud
叫我阿柒啊1 天前
Java全栈开发工程师的实战面试:从技术到业务场景
java·微服务·vue3·springboot·前端开发·后端开发·数据库优化
Jacobshash1 天前
SpringCloud框架组件梳理
后端·spring·spring cloud
boy快快长大1 天前
使用LoadBalancer替换Ribbon(五)
后端·spring cloud·ribbon
阿提说说1 天前
一个工程多Module的微服务项目,如何在GitLab中配置CI/CD
ci/cd·微服务·gitlab