【SpringBoot技术专题】「实战指南」从实战开发角度去分析操作RestTemplate的应用及使用技巧

简介: 当你的应用程序需要访问远程接口时,很容易被不同的浏览器和API调用协议弄晕。幸运的是,Spring框架已为我们提供了一个简单而功能强大的RestTemplate工具,它可以轻松地处理这些基础任务并提供一个简单的方式来访问各种API。

前提介绍

当你的应用程序需要访问远程接口时,很容易被不同的浏览器和API调用协议弄晕。幸运的是,Spring框架已为我们提供了一个简单而功能强大的RestTemplate工具,它可以轻松地处理这些基础任务并提供一个简单的方式来访问各种API。

RestTemplate是Spring Framework中的一个用于RESTful Web Services的HTTP客户端,是Spring Web模块中的一部分。使用RestTemplate,我们可以消费Web服务,发送HTTP请求,并的到HTTP响应。它支持许多HTTP操作,如GET,POST,PUT,DELETE和PATCH,并可以通过各种协议访问不同的API和Web服务。

使用RestTemplate的好处包括:

  1. 不需要自己编写低级别的HttpURLConnection代码,它已经为我们做好了这些工作。
  2. RestTemplate默认使用Jackson JSON库进行对象序列化和反序列化。
  3. RestTemplate在异常处理和错误处理方面提供了优秀的支持。
  4. RestTemplate可以支持Ribbon,并且可以进行客户端负载平衡(如果有多个实例的话)。

接下来,我们将通过一个简单的示例来说明如何使用RestTemplate。

首先,让我们假设我们有一个可以向我们提供天气预报信息的RESTful Web服务。Web服务只支持HTTP GET请求,并返回JSON格式的数据。我们需要通过访问URL地址来获取数据。

现在,我们需要一个方式来访问这个Web服务,并通过RestTemplate将结果映射到我们的Java类中。下面是一个非常简单的代码块,您可以将其添加到您的应用程序中,以开始使用RestTemplate。

RestTemplate restTemplate = new RestTemplate();
WeatherReport report = restTemplate.getForObject("http://api.weather.com/report", WeatherReport.class);

使用RestTemplate的第一步是创建一个RestTemplate实例。在这个例子中,我们使用new关键字简单地实例化了一个RestTemplate对象。

接下来,在此示例中,我们使用getForObject方法来从URL中检索WeatherReport对象。当我们调用这个方法时,RestTemplate将自动使用Jackson JSON库将JSON响应映射到我们的WeatherReport类。需要注意的是,这里我们没有指定使用任何参数,也没有传递任何Http请求头,使用RestTemplate像这样发送数据是非常方便的。

使用RestTemplate,我们还可以执行POST,PUT,DELETE请求。例如,我们可以使用RestTemplate执行一个POST请求,以创建一个新记录:

RestTemplate restTemplate = new RestTemplate();
String addRecordUrl = "http://api.example.com/record";
Record record = // create new record
HttpEntity<Record> request = new HttpEntity<>(record, headers);
Record result = restTemplate.postForObject(addRecordUrl, request, Record.class);

在这个例子中,我们为创建请求提供了一个HttpEntity,并将其传递给postForObject方法。这将告诉RestTemplate我们想将一个对象提交到URL,同时还提供了一些Http请求头以满足API规范。RestTemplate将发送POST请求,并将请求体映射到我们提供的Record对象。最后一个参数是我们所期望的响应对象类型。根据我们的例子,我们期望响应是Record对象,所以我们将Record.class传递给postForObject方法。

总之,RestTemplate提供了一个可以轻松地访问RESTful Web服务的HTTP客户端,规避了复杂的低级HTTP访问代码,使我们可以为实现不同的HTTP操作节省大量的开发时间。希望这篇文章可以帮助您更好地了解如何使用RestTemplate。


RestTemplate的详细功能介绍

Spring提供了一种简单便捷的模板类RestTemplate来调用RESTful 接口。它提供了多种便捷访问HTTP服务的方法,能够大大提高客户端的编写效率。

在这里插入图片描述

RESTful API接口

@RestController
@Slf4j
public class RestfulController {
    
    // GET请求,不带参
    @GetMapping(value = "/getUser1", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public User getUser1() {
        return new User(1L, "zhaoxb");
    }
    // GET请求,带参。
    @GetMapping(value = "/getUser2", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public User getUser2(User user) {
        log.info("getUser2:{}", JSONUtil.toJsonPrettyStr(user));
        return user;
    }
    // POST请求,带参。
    @PostMapping(value = "/postUser", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public User postUser(User user) {
        log.info("postUser:{}", JSONUtil.toJsonPrettyStr(user));
        return user;
    }
    // POST请求,带有请求体。
    @PostMapping(value = "/postBody", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public User postBody(@RequestBody User user) {
        log.info("postBody:{}", JSONUtil.toJsonPrettyStr(user));
        return user;
    }
}

实体类,需要提供有参和无参构造。

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private Long id;
    private String name;
}

RestTemplate配置类

@Configuration
public class RestConfig {
    @Bean
    public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) {
        RestTemplate restTemplate = restTemplateBuilder
                .setConnectTimeout(Duration.ofMillis(5000L))
                .setReadTimeout(Duration.ofMillis(30000L))
                .build();
        return restTemplate;
    }
}

发送GET请求

getForEntity方法,不带参
@ActiveProfiles("test")
@RunWith(SpringRunner.class)
@SpringBootTest
@Slf4j
public class RestfulGetTests {
    
    @Autowired
    private RestTemplate restTemplate;
    @Test
    public void getForEntity1() {
        ResponseEntity<String> responseEntity = restTemplate.
            getForEntity("http://127.0.0.1:31000/getUser1", String.class);
        log.info("响应码:{}", responseEntity.getStatusCodeValue());
        log.info("响应体:{}", JSONUtil.toJsonPrettyStr(responseEntity.getBody()));
    }
}
请求结果信息
响应码:200
响应体:{
    "name": "zhaoxb",
    "id": 1
}
getForEntity方法,数字占位符方式传参
@Test
public void getForEntity2() {
    ResponseEntity<String> responseEntity = restTemplate.
        getForEntity("http://127.0.0.1:31000/getUser2?id={1}&name={2}", String.class, 2, "zhaoxb");
    log.info("响应体:{}", JSONUtil.toJsonPrettyStr(responseEntity.getBody()));
}

用一个数字做占位符。最后是一个可变长度的参数,用来替换前面的占位符。

响应体:{
    "name": "zhaoxb",
    "id": 2
}
getForEntity方法,map占位符方式传参
@Test
public void getForEntity3() {
    Map<String, Object> map = new HashMap<>();
    map.put("id", 3);
    map.put("name", "zhaoxb");
    ResponseEntity<String> responseEntity = restTemplate.
        getForEntity("http://127.0.0.1:31000/getUser2?id={id}&name={name}", String.class, map);
    log.info("响应体:{}", JSONUtil.toJsonPrettyStr(responseEntity.getBody()));
}

使用 name={name} 这种形式。最后一个参数是map,map的key为前边占位符的名字,value为实际参数值。

响应体:{
    "name": "zhaoxb",
    "id": 3
}
getForEntity方法,返回对象
@Test
public void getForEntity4() {
    ResponseEntity<User> responseEntity = restTemplate.getForEntity("http://127.0.0.1:31000/getUser1", User.class);
    log.info("响应体:{}", JSONUtil.toJsonPrettyStr(responseEntity.getBody()));
}
响应体:{
    "name": "zhaoxb",
    "id": 1
}
getForObject方法,直接返回对象
@Test
public void getForObject() {
    User User = restTemplate.getForObject("http://127.0.0.1:31000/getUser1", User.class);
    log.info("响应体:{}", JSONUtil.toJsonPrettyStr(User));
}

getForObject是对getForEntity函数的进一步封装,只关注返回消息的实体内容。

响应体:{
    "name": "zhaoxb",
    "id": 1
}

发送POST请求

用post方法发送带参的请求时,Map不能被定义为 HashMap、LinkedHashMap,而应被定义为 LinkedMultiValueMap,这样参数才能成功传递到后台。

postForEntity方法
@Test
public void postForEntity() {
    MultiValueMap<String, Object> multiValueMap = new LinkedMultiValueMap<>();
    multiValueMap.add("id", 5);
    multiValueMap.add("name", "zhaoxb");
    ResponseEntity<User> responseEntity = restTemplate.postForEntity("http://127.0.0.1:31000/postUser",
     multiValueMap, User.class);
    log.info("响应码:{}", responseEntity.getStatusCodeValue());
    log.info("响应体:{}", JSONUtil.toJsonPrettyStr(responseEntity.getBody()));
}
结果
响应体:{
    "name": "zhaoxb",
    "id": 5
}
postForObject方法
@Test
public void postForObject() {
    MultiValueMap<String, Object> multiValueMap = new LinkedMultiValueMap<>();
    multiValueMap.add("id", 6);
    multiValueMap.add("name", "zhaoxb");
    User user = restTemplate.postForObject("http://127.0.0.1:31000/postUser", multiValueMap, User.class);
    log.info("响应体:{}", JSONUtil.toJsonPrettyStr(user));
}

和 getForObject 相对应,只关注返回的消息体。

响应体:{
    "name": "zhaoxb",
    "id": 6
}
postForObject方法,带有请求体body
@Test
public void postForObject2() {
    User reqUser = new User(10L, "zhaoxb");
    User user = restTemplate.postForObject("http://127.0.0.1:31000/postBody", reqUser, User.class);
    log.info("响应体:{}", JSONUtil.toJsonPrettyStr(user));
}

RestTemplate底层实现序列化和反序列化。

响应体:{
    "name": "zhaoxb",
    "id": 10
}

exchange方法

@Test
public void exchange() {
    MultiValueMap<String, Object> multiValueMap = new LinkedMultiValueMap<>();
    multiValueMap.add("id", 7);
    multiValueMap.add("name", "zhaoxb");
    HttpEntity<MultiValueMap<String, Object>> httpEntity = new HttpEntity<>(multiValueMap);
    HttpEntity<MultiValueMap<String, Object>> httpEntityWithHeaders = new HttpEntity<>(multiValueMap, new HttpHeaders());
    ResponseEntity<User> responseEntity = restTemplate.exchange("http://127.0.0.1:31000/postUser", HttpMethod.POST, httpEntity, User.class);
    log.info("响应体:{}", JSONUtil.toJsonPrettyStr(responseEntity.getBody()));
}

HttpEntity还支持带有HTTP请求头的构造方法。

响应体:{
    "name": "zhaoxb",
    "id": 7
}

用RestTemplate发送PUT、PATCH、DELETE方法与GET、POST方法非常类似,这里不做展开。

自定义template

自定义HTTP源

  • ClientHttpRequestFactory是Spring定义的一个接口,用于生产ClientHttpRequest对象,RestTemplate只是模板类,抽象

了很多调用方法,而底层真正使用何种框架发送HTTP请求是通过ClientHttpRequestFactory指定的。

  • RestTemplate默认使用的是SimpleClientHttpRequestFactory,其内部使用的是JDK的java.net.HttpURLConnection创建

底层连接,默认是没有连接池的。可以通过 setRequestFactory 函数设置不同的HTTP源,比如 Apache HttpComponents、Netty

和OkHttp。

<dependencies>
    <dependency>
        <groupId>org.apache.httpcomponents</groupId>
        <artifactId>httpclient</artifactId>
    </dependency>
</dependencies>
@Configuration
public class RestConfig {
    @Bean
    public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) {
        RestTemplate restTemplate = restTemplateBuilder
                .setConnectTimeout(Duration.ofMillis(5000L))
                .setReadTimeout(Duration.ofMillis(30000L))
                .requestFactory(() -> clientHttpRequestFactory())
                .build();
        return restTemplate;
    }

    @Bean
    public ClientHttpRequestFactory clientHttpRequestFactory() {
        HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
        //开始设置连接池
        PoolingHttpClientConnectionManager poolingHttpClientConnectionManager = new PoolingHttpClientConnectionManager();
        poolingHttpClientConnectionManager.setMaxTotal(100);  //最大连接数
        poolingHttpClientConnectionManager.setDefaultMaxPerRoute(20);  //同路由并发数
        httpClientBuilder.setConnectionManager(poolingHttpClientConnectionManager);

        HttpClient httpClient = httpClientBuilder.build();
        // httpClient连接配置
        HttpComponentsClientHttpRequestFactory clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory(httpClient);
        // 设置超时,如果 RestTemplateBuilder 已经设置,这里就不需要设置
//        clientHttpRequestFactory.setConnectTimeout(5 * 1000); // 连接超时
//        clientHttpRequestFactory.setReadTimeout(30 * 1000); // 数据读取超时时间
        clientHttpRequestFactory.setConnectionRequestTimeout(30 * 1000); //连接不够用的等待时间
        return clientHttpRequestFactory;
    }
}

自定义messageConverter

  • RestTemplate默认使用 jackson 来实现序列化和反序列化,默认情况下会注册MIME类型的转换器,但可以通

过 setMessageConverters 函数指定其他类型的转化器。

  • 这里其实也可以用FastJson库的FastJsonHttpMessageConverter4类来做转换器,只是近些年FastJson屡爆漏洞,还是建议

用默认的jackson来实现。

相关文章
|
1月前
|
JavaScript 安全 Java
如何使用 Spring Boot 和 Ant Design Pro Vue 实现动态路由和菜单功能,快速搭建前后端分离的应用框架
本文介绍了如何使用 Spring Boot 和 Ant Design Pro Vue 实现动态路由和菜单功能,快速搭建前后端分离的应用框架。首先,确保开发环境已安装必要的工具,然后创建并配置 Spring Boot 项目,包括添加依赖和配置 Spring Security。接着,创建后端 API 和前端项目,配置动态路由和菜单。最后,运行项目并分享实践心得,包括版本兼容性、安全性、性能调优等方面。
137 1
|
1月前
|
人工智能 自然语言处理 前端开发
SpringBoot + 通义千问 + 自定义React组件:支持EventStream数据解析的技术实践
【10月更文挑战第7天】在现代Web开发中,集成多种技术栈以实现复杂的功能需求已成为常态。本文将详细介绍如何使用SpringBoot作为后端框架,结合阿里巴巴的通义千问(一个强大的自然语言处理服务),并通过自定义React组件来支持服务器发送事件(SSE, Server-Sent Events)的EventStream数据解析。这一组合不仅能够实现高效的实时通信,还能利用AI技术提升用户体验。
161 2
|
1月前
|
Java Maven Docker
gitlab-ci 集成 k3s 部署spring boot 应用
gitlab-ci 集成 k3s 部署spring boot 应用
|
15天前
|
JavaScript 安全 Java
如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个具有动态路由和菜单功能的前后端分离应用。
本文介绍了如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个具有动态路由和菜单功能的前后端分离应用。首先,创建并配置 Spring Boot 项目,实现后端 API;然后,使用 Ant Design Pro Vue 创建前端项目,配置动态路由和菜单。通过具体案例,展示了如何快速搭建高效、易维护的项目框架。
94 62
|
13天前
|
JavaScript 安全 Java
如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个前后端分离的应用框架,实现动态路由和菜单功能
本文介绍了如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个前后端分离的应用框架,实现动态路由和菜单功能。首先,确保开发环境已安装必要的工具,然后创建并配置 Spring Boot 项目,包括添加依赖和配置 Spring Security。接着,创建后端 API 和前端项目,配置动态路由和菜单。最后,运行项目并分享实践心得,帮助开发者提高开发效率和应用的可维护性。
32 2
|
22天前
|
自然语言处理 Java API
Spring Boot 接入大模型实战:通义千问赋能智能应用快速构建
【10月更文挑战第23天】在人工智能(AI)技术飞速发展的今天,大模型如通义千问(阿里云推出的生成式对话引擎)等已成为推动智能应用创新的重要力量。然而,对于许多开发者而言,如何高效、便捷地接入这些大模型并构建出功能丰富的智能应用仍是一个挑战。
89 6
|
1月前
|
JavaScript 安全 Java
如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个具有动态路由和菜单功能的前后端分离应用
【10月更文挑战第8天】本文介绍了如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个具有动态路由和菜单功能的前后端分离应用。首先,通过 Spring Initializr 创建并配置 Spring Boot 项目,实现后端 API 和安全配置。接着,使用 Ant Design Pro Vue 脚手架创建前端项目,配置动态路由和菜单,并创建相应的页面组件。最后,通过具体实践心得,分享了版本兼容性、安全性、性能调优等注意事项,帮助读者快速搭建高效且易维护的应用框架。
39 3
|
1月前
|
JavaScript 安全 Java
如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个具有动态路由和菜单功能的前后端分离应用
【10月更文挑战第7天】本文介绍了如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个具有动态路由和菜单功能的前后端分离应用。首先,通过 Spring Initializr 创建 Spring Boot 项目并配置 Spring Security。接着,实现后端 API 以提供菜单数据。在前端部分,使用 Ant Design Pro Vue 脚手架创建项目,并配置动态路由和菜单。最后,启动前后端服务,实现高效、美观且功能强大的应用框架。
37 2
|
1月前
|
Java BI API
spring boot 整合 itextpdf 导出 PDF,写入大文本,写入HTML代码,分析当下导出PDF的几个工具
这篇文章介绍了如何在Spring Boot项目中整合iTextPDF库来导出PDF文件,包括写入大文本和HTML代码,并分析了几种常用的Java PDF导出工具。
372 0
spring boot 整合 itextpdf 导出 PDF,写入大文本,写入HTML代码,分析当下导出PDF的几个工具
|
1月前
|
搜索推荐 Java UED
SpringBoot 自定义启动画面:打造个性化应用启动体验
【10月更文挑战第7天】在软件开发中,细节往往能够体现一个团队的专业性和对用户体验的关注。SpringBoot作为快速构建Spring应用的框架,其简洁的启动流程和强大的功能深受开发者喜爱。然而,默认的启动画面可能略显单调,无法充分展示应用的特色或品牌。本文将详细介绍如何为SpringBoot应用自定义启动画面,让应用在启动时就能给人留下深刻印象。
50 1