程序员scholar 程序员scholar
首页
  • Java 基础

    • JavaSE
    • JavaIO
    • JavaAPI速查
  • Java 高级

    • JUC
    • JVM
    • Java新特性
    • 设计模式
  • Web 开发

    • Servlet
    • Java网络编程
  • Web 标准

    • HTML
    • CSS
    • JavaScript
  • 前端框架

    • Vue2
    • Vue3
    • Vue3 + TS
    • 微信小程序
    • uni-app
  • 工具与库

    • jQuery
    • Ajax
    • Axios
    • Webpack
    • Vuex
    • WebSocket
    • 第三方登录
  • 后端与语言扩展

    • ES6
    • Typescript
    • node.js
  • Element-UI
  • Apache ECharts
  • 数据结构
  • HTTP协议
  • HTTPS协议
  • 计算机网络
  • Linux常用命令
  • Windows常用命令
  • SQL数据库

    • MySQL
    • MySQL速查
  • NoSQL数据库

    • Redis
    • ElasticSearch
  • 数据库

    • MyBatis
    • MyBatis-Plus
  • 消息中间件

    • RabbitMQ
  • 服务器

    • Nginx
  • Spring框架

    • Spring6
    • SpringMVC
    • SpringBoot
    • SpringSecurity
  • SpringCould微服务

    • SpringCloud基础
    • 微服务之DDD架构思想
  • 日常必备

    • 开发常用工具包
    • Hutoll工具包
    • IDEA常用配置
    • 开发笔记
    • 日常记录
    • 项目部署
    • 网站导航
    • 产品学习
    • 英语学习
  • 代码管理

    • Maven
    • Git教程
    • Git小乌龟教程
  • 运维工具

    • Docker
    • Jenkins
    • Kubernetes
  • 算法笔记

    • 算法思想
    • 刷题笔记
  • 面试问题常见

    • 十大经典排序算法
    • 面试常见问题集锦
关于
GitHub (opens new window)
首页
  • Java 基础

    • JavaSE
    • JavaIO
    • JavaAPI速查
  • Java 高级

    • JUC
    • JVM
    • Java新特性
    • 设计模式
  • Web 开发

    • Servlet
    • Java网络编程
  • Web 标准

    • HTML
    • CSS
    • JavaScript
  • 前端框架

    • Vue2
    • Vue3
    • Vue3 + TS
    • 微信小程序
    • uni-app
  • 工具与库

    • jQuery
    • Ajax
    • Axios
    • Webpack
    • Vuex
    • WebSocket
    • 第三方登录
  • 后端与语言扩展

    • ES6
    • Typescript
    • node.js
  • Element-UI
  • Apache ECharts
  • 数据结构
  • HTTP协议
  • HTTPS协议
  • 计算机网络
  • Linux常用命令
  • Windows常用命令
  • SQL数据库

    • MySQL
    • MySQL速查
  • NoSQL数据库

    • Redis
    • ElasticSearch
  • 数据库

    • MyBatis
    • MyBatis-Plus
  • 消息中间件

    • RabbitMQ
  • 服务器

    • Nginx
  • Spring框架

    • Spring6
    • SpringMVC
    • SpringBoot
    • SpringSecurity
  • SpringCould微服务

    • SpringCloud基础
    • 微服务之DDD架构思想
  • 日常必备

    • 开发常用工具包
    • Hutoll工具包
    • IDEA常用配置
    • 开发笔记
    • 日常记录
    • 项目部署
    • 网站导航
    • 产品学习
    • 英语学习
  • 代码管理

    • Maven
    • Git教程
    • Git小乌龟教程
  • 运维工具

    • Docker
    • Jenkins
    • Kubernetes
  • 算法笔记

    • 算法思想
    • 刷题笔记
  • 面试问题常见

    • 十大经典排序算法
    • 面试常见问题集锦
关于
GitHub (opens new window)
npm

(进入注册为作者充电)

  • Spring Boot

    • Spring Boot - 自动配置
    • Spring Boot - 自定义starter
    • Spring Boot - 配置文件
    • Spring Boot - 自定义SpringApplication
    • Spring Boot - 生命周期与事件
    • Spring Boot - 事件驱动
    • Spring Boot - Bean 加载方式
    • Spring Boot - 容器资源感知与获取
    • Spring Boot - 定时任务
    • Spring Boot - 异步任务
    • Spring Boot - 内置日志
    • Spring Boot - 函数式 Web
    • Spring Boot - 响应式远程调用
    • Spring Boot - 接口文档
    • Spring Boot - 单元测试
    • Spring Boot - 内容协商
    • Spring Boot - 参数校验
    • Spring Boot - HTTP客户端工具
      • 1. RestTemplate简介
      • 2. RestTemplate的创建方式
        • 2.1 使用RestTemplateBuilder构建器创建(推荐)
        • 2.2 直接使用构造方法创建
        • 2.3 两种方式的选择
      • 3. RestTemplate API使用
        • 3.1 GET请求
        • 3.1.1 getForEntity方法
        • 3.1.2 getForObject方法
        • 3.2 POST请求
        • 3.2.1 postForEntity方法
        • 3.2.2 postForObject方法
        • 3.2.3 postForLocation方法
        • 3.2.4 使用表单方式提交POST请求
        • 3.3 PUT请求
        • 3.4 DELETE请求
        • 3.5 HEAD请求
        • 3.6 OPTIONS请求
        • 3.7 EXCHANGE方法
        • 使用exchange发送GET请求带请求体
        • 3.8 EXECUTE方法
      • 4. URL参数传递技巧
        • 4.1 路径变量(Path Variables)
        • 4.2 查询参数(Query Parameters)
      • 5. 异常处理
      • 6. 高级应用
        • 6.1 使用ResponseErrorHandler处理错误
        • 6.2 使用HttpMessageConverter自定义序列化和反序列化
        • 6.3 配置连接池
    • Spring Boot - 控制器请求映射
    • Spring Boot - 请求参数接收
    • Spring Boot - 通用响应类
    • Spring Boot - 全局异常处理
    • Spring Boot - 整合Druid
    • Spring Boot - 整合Thymeleaf
    • Spring Boot - 国际化实现
    • Spring Boot - 自定义注解
  • Spring高级
  • Spring Boot
scholar
2022-12-12
目录

Spring Boot - HTTP客户端工具

# Spring Boot - HTTP客户端工具

# 1. RestTemplate简介

RestTemplate是Spring框架提供的一个强大HTTP客户端工具,专为调用RESTful服务而设计。它简化了与HTTP服务的通信方式,统一了RESTful标准,封装了HTTP连接细节。与传统的HttpClient相比,RestTemplate提供了更加优雅且符合Spring风格的API。

RestTemplate的设计理念与Spring中其他模板类(如JdbcTemplate)一致,通过提供默认行为来简化复杂任务的执行。它默认依赖JDK的HttpURLConnection,但可以通过setRequestFactory方法灵活切换为Apache HttpComponent、Netty或OKHttp等其他HTTP库。

RestTemplate的主要方法与HTTP协议的方法紧密对应:HEAD、GET、POST、PUT、DELETE、OPTIONS等。例如,它提供了headForHeaders()、getForObject()、postForEntity()、put()和delete()等方法,便于开发者直观地进行HTTP操作。

# 2. RestTemplate的创建方式

Spring框架虽然提供了RestTemplate类,但并未将其自动加入Spring容器中,需要开发者手动配置。下面介绍两种常用的创建方式:

# 2.1 使用RestTemplateBuilder构建器创建(推荐)

通过Spring Boot提供的RestTemplateBuilder,可以灵活配置连接参数:

@Configuration
public class WebConfiguration {
    @Bean
    public RestTemplate restTemplate(RestTemplateBuilder builder){
        return builder
                // 设置连接超时时间(5秒)
                .setConnectTimeout(Duration.ofSeconds(5))
                // 设置读取数据超时时间(5秒)
                .setReadTimeout(Duration.ofSeconds(5))
                // 设置API访问认证信息(根据实际情况替换用户名和密码)
                .basicAuthentication("username","password")
                // 设置根URI(所有请求都将以此URL为基础)
                .rootUri("https://api.example.com/")
                // 构建RestTemplate实例
                .build();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

这种方式的优势在于可以详细配置RestTemplate的参数,使其更符合特定业务需求。

添加自定义拦截器

使用RestTemplateBuilder还可以添加自定义拦截器,对请求和响应进行拦截处理:

@Slf4j
public class CustomClientHttpRequestInterceptor implements ClientHttpRequestInterceptor {
    @Override
    public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
        // 记录请求详情
        logRequestDetails(request, body);
        // 执行实际请求
        ClientHttpResponse response = execution.execute(request, body);
        // 记录响应详情
        logResponseDetails(response);
        
        return response;
    }

    // 记录请求详情的私有方法
    private void logRequestDetails(HttpRequest request, byte[] body){
        log.debug("请求头信息: {}", request.getHeaders());
        log.debug("请求体内容: {}", new String(body, StandardCharsets.UTF_8));
        log.debug("请求方法: {}:{}", request.getMethod(), request.getMethodValue());
    }

    // 记录响应详情的私有方法
    private void logResponseDetails(ClientHttpResponse response) throws IOException {
        log.debug("状态码: {}", response.getStatusCode());
        log.debug("状态文本: {}", response.getStatusText());
        log.debug("响应头: {}", response.getHeaders());
        log.debug("响应体: {}", StreamUtils.copyToString(response.getBody(), StandardCharsets.UTF_8));
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

将自定义拦截器添加到RestTemplate:

@Configuration
public class WebConfiguration {
    @Bean
    public RestTemplate restTemplate(RestTemplateBuilder builder){
        return builder
                // 其他配置...
                // 添加自定义请求拦截器
                .additionalInterceptors(new CustomClientHttpRequestInterceptor())
                // 构建
                .build();
    }    
}
1
2
3
4
5
6
7
8
9
10
11
12

注意:请求和响应的流只能被读取一次。当在拦截器中读取了response后,返回的response将无法再次读取相同内容,类似于@ResponseBody的工作方式。

# 2.2 直接使用构造方法创建

也可以通过RestTemplate的构造方法直接创建实例:

@Configuration
public class WebConfiguration {
    @Bean
    public RestTemplate restTemplate(){
        // 创建一个使用默认配置的RestTemplate实例
        return new RestTemplate();
    }
}
1
2
3
4
5
6
7
8

RestTemplate有三种构造方法:

  • 无参构造:使用默认配置
  • 指定ClientHttpRequestFactory的构造方法:可自定义HTTP请求工厂
  • 指定HttpMessageConverter的构造方法:可自定义HTTP消息转换器

# 2.3 两种方式的选择

两种创建方式各有优势:

  • RestTemplateBuilder方式提供更多自定义选项,配置更灵活完善
  • 构造方法方式更简洁,适合基本使用场景,特别是需要创建多个不同根地址的RestTemplate实例时

# 3. RestTemplate API使用

RestTemplate提供了丰富的API,主要可分为以下几类:

  • GET请求:获取资源
  • POST请求:创建资源
  • PUT请求:更新资源
  • DELETE请求:删除资源
  • HEAD请求:获取头信息
  • OPTIONS请求:获取支持的方法
  • EXCHANGE请求:通用请求方法
  • EXECUTE请求:最底层执行方法

# 3.1 GET请求

RestTemplate提供了两种发送GET请求的方法:

# 3.1.1 getForEntity方法

获取完整的响应实体,包含状态码、头信息和响应体:

/**
 * 使用getForEntity获取完整响应信息
 */
public void getForEntityDemo() {
    // 发送GET请求并获取完整响应实体
    ResponseEntity<String> response = restTemplate.getForEntity("https://api.example.com/users/{id}", String.class, 123);
    
    // 检查响应状态码
    if(response.getStatusCode() == HttpStatus.OK) {
        // 获取响应状态信息
        System.out.println("状态码:" + response.getStatusCode());
        System.out.println("状态码值:" + response.getStatusCodeValue());
        
        // 获取响应头信息
        HttpHeaders headers = response.getHeaders();
        System.out.println("响应头:" + headers);
        
        // 获取响应体内容
        System.out.println("响应内容:" + response.getBody());
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

getForEntity方法参数说明:

  1. 第一个参数:请求的URL,可包含路径变量占位符{变量名}
  2. 第二个参数:响应体类型
  3. 第三个参数(可选):URL路径变量值,可以是可变参数或Map

使用Map传递URL参数:

/**
 * 使用Map传递URL参数
 */
public void getWithMapParamsDemo() {
    // 创建参数Map
    Map<String, Object> urlParams = new HashMap<>();
    urlParams.put("id", 123);
    urlParams.put("name", "张三");
    
    // 使用Map传递URL参数
    ResponseEntity<User> response = restTemplate.getForEntity(
        "https://api.example.com/users/{id}/profile/{name}", 
        User.class, 
        urlParams
    );
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 3.1.2 getForObject方法

直接获取响应体内容,忽略状态码和头信息:

/**
 * 使用getForObject直接获取响应内容
 */
public void getForObjectDemo() {
    // 直接获取响应体内容,转换为User对象
    User user = restTemplate.getForObject(
        "https://api.example.com/users/{id}", 
        User.class, 
        123
    );
    
    System.out.println("用户信息: " + user);
}
1
2
3
4
5
6
7
8
9
10
11
12
13

当仅需要响应内容而不关心响应状态和头信息时,getForObject方法更加简洁。

# 3.2 POST请求

RestTemplate提供了三种发送POST请求的方法:

# 3.2.1 postForEntity方法

发送POST请求并获取完整响应实体:

/**
 * 使用postForEntity发送POST请求
 */
public void postForEntityDemo() {
    // 创建请求对象
    User newUser = new User();
    newUser.setName("李四");
    newUser.setAge(28);
    
    // 发送POST请求并获取完整响应
    ResponseEntity<User> response = restTemplate.postForEntity(
        "https://api.example.com/users", 
        newUser,  // 请求体
        User.class  // 响应体类型
    );
    
    // 处理响应
    System.out.println("响应状态: " + response.getStatusCode());
    System.out.println("创建的用户: " + response.getBody());
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 3.2.2 postForObject方法

发送POST请求并直接获取响应体:

/**
 * 使用postForObject发送POST请求
 */
public void postForObjectDemo() {
    // 创建请求对象
    User newUser = new User();
    newUser.setName("王五");
    newUser.setAge(35);
    
    // 发送POST请求并直接获取响应体
    User createdUser = restTemplate.postForObject(
        "https://api.example.com/users", 
        newUser,  // 请求体
        User.class  // 响应体类型
    );
    
    System.out.println("创建的用户: " + createdUser);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 3.2.3 postForLocation方法

发送POST请求并获取新资源的URI:

/**
 * 使用postForLocation发送POST请求
 */
public void postForLocationDemo() {
    // 创建请求对象
    User newUser = new User();
    newUser.setName("赵六");
    newUser.setAge(40);
    
    // 发送POST请求并获取新创建资源的URI
    URI resourceUri = restTemplate.postForLocation(
        "https://api.example.com/users", 
        newUser  // 请求体
    );
    
    System.out.println("新资源的URI: " + resourceUri);
    // 可以使用此URI进行后续操作
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

postForLocation特别适用于创建资源后需要跳转到新资源的场景。

# 3.2.4 使用表单方式提交POST请求

除了直接提交对象外,还可以使用表单方式提交POST请求:

/**
 * 使用表单方式提交POST请求
 */
public void postFormDataDemo() {
    // 设置请求头,指定content-type为application/x-www-form-urlencoded
    HttpHeaders headers = new HttpHeaders();
    headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
    
    // 添加自定义header信息
    headers.set("Authorization", "Bearer eyJhbGciOiJIUzI1NiJ9...");
    headers.set("AppId", "myapp123");
    
    // 创建表单参数
    MultiValueMap<String, String> formData = new LinkedMultiValueMap<>();
    formData.add("username", "zhangsan");
    formData.add("password", "123456");
    formData.add("remember", "true");
    
    // 将请求头和表单数据组合成一个HttpEntity
    HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(formData, headers);
    
    // 发送POST请求
    ResponseEntity<String> response = restTemplate.postForEntity(
        "https://api.example.com/login", 
        requestEntity, 
        String.class
    );
    
    System.out.println("登录响应: " + response.getBody());
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

表单提交需要使用以下几个关键类:

  • HttpHeaders: 封装HTTP请求头信息
  • MultiValueMap: 封装表单参数,支持一个key对应多个value
  • HttpEntity: 将请求头和请求体封装在一起

# 3.3 PUT请求

PUT请求用于更新资源:

/**
 * 发送PUT请求更新资源
 */
public void putRequestDemo() {
    // 创建更新的对象
    User updatedUser = new User();
    updatedUser.setName("张三(已更新)");
    updatedUser.setAge(31);
    
    // 发送PUT请求,无返回值
    restTemplate.put(
        "https://api.example.com/users/{id}", 
        updatedUser,  // 请求体
        123  // URL变量
    );
    
    System.out.println("用户信息已更新");
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

PUT方法通常不返回内容,只需提供URL和要更新的对象。

# 3.4 DELETE请求

DELETE请求用于删除资源:

/**
 * 发送DELETE请求删除资源
 */
public void deleteRequestDemo() {
    // 发送DELETE请求,无返回值
    restTemplate.delete("https://api.example.com/users/{id}", 123);
    
    System.out.println("用户已删除");
}
1
2
3
4
5
6
7
8
9

与PUT类似,DELETE通常不返回内容,RestTemplate的delete方法不支持携带请求体。

# 3.5 HEAD请求

HEAD请求用于获取资源的头信息:

/**
 * 发送HEAD请求获取头信息
 */
public void headRequestDemo() {
    // 获取资源的头信息
    HttpHeaders headers = restTemplate.headForHeaders("https://api.example.com/users");
    
    // 查看特定头信息
    System.out.println("Content-Type: " + headers.getContentType());
    System.out.println("Content-Length: " + headers.getContentLength());
    System.out.println("所有头信息: " + headers);
}
1
2
3
4
5
6
7
8
9
10
11
12

HEAD请求只返回头信息,不返回响应体,适用于检查资源是否存在或获取资源元信息。

# 3.6 OPTIONS请求

OPTIONS请求用于获取服务器支持的HTTP方法:

/**
 * 发送OPTIONS请求获取支持的方法
 */
public void optionsRequestDemo() {
    // 获取服务器支持的HTTP方法
    Set<HttpMethod> allowedMethods = restTemplate.optionsForAllow("https://api.example.com/users");
    
    // 输出支持的方法
    System.out.println("支持的HTTP方法: " + allowedMethods);
    
    // 检查是否支持特定方法
    if (allowedMethods.contains(HttpMethod.DELETE)) {
        System.out.println("支持DELETE方法");
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

OPTIONS请求通常用于CORS预检请求或检查API支持的操作。

# 3.7 EXCHANGE方法

exchange是一个更通用的方法,可以指定HTTP方法、请求头和请求体:

/**
 * 使用exchange方法发送自定义请求
 */
public void exchangeDemo() {
    // 创建请求头
    HttpHeaders headers = new HttpHeaders();
    headers.setContentType(MediaType.APPLICATION_JSON);
    headers.setBearerAuth("eyJhbGciOiJIUzI1NiJ9...");
    
    // 创建请求体
    User user = new User();
    user.setName("赵七");
    user.setAge(45);
    
    // 创建请求实体
    HttpEntity<User> requestEntity = new HttpEntity<>(user, headers);
    
    // 发送请求并获取响应
    ResponseEntity<User> response = restTemplate.exchange(
        "https://api.example.com/users/{id}",
        HttpMethod.PUT,  // 指定HTTP方法
        requestEntity,  // 请求实体
        User.class,  // 响应类型
        789  // URL变量
    );
    
    System.out.println("响应状态: " + response.getStatusCode());
    System.out.println("响应体: " + response.getBody());
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

exchange方法的优势:

  1. 可以动态指定HTTP方法
  2. 可以同时设置请求头和请求体
  3. 支持泛型作为返回类型

特别适用于需要自定义HTTP方法或需要添加复杂请求头的场景。

# 使用exchange发送GET请求带请求体

标准HTTP规范中,GET请求通常不带请求体,但某些特殊API可能需要这种非标准用法:

/**
 * 使用exchange发送带请求体的GET请求
 */
public void getWithRequestBodyDemo() {
    // 创建请求头
    HttpHeaders headers = new HttpHeaders();
    headers.setContentType(MediaType.APPLICATION_JSON);
    
    // 创建请求体
    Map<String, Object> queryParams = new HashMap<>();
    queryParams.put("filter", "active");
    queryParams.put("complex", true);
    
    // 创建请求实体
    HttpEntity<Map<String, Object>> requestEntity = new HttpEntity<>(queryParams, headers);
    
    // 使用exchange发送带请求体的GET请求
    ResponseEntity<User[]> response = restTemplate.exchange(
        "https://api.example.com/users/search",
        HttpMethod.GET,
        requestEntity,
        User[].class
    );
    
    System.out.println("找到" + response.getBody().length + "个用户");
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

注意:虽然技术上可以实现,但发送带请求体的GET请求不符合HTTP标准,某些服务器可能不支持。

# 3.8 EXECUTE方法

execute是RestTemplate的最底层方法,所有其他方法最终都会调用它:

/**
 * 使用execute方法发送请求
 */
public void executeDemo() {
    // 创建请求实体
    User user = new User();
    user.setName("孙八");
    user.setAge(50);
    
    HttpHeaders headers = new HttpHeaders();
    headers.setContentType(MediaType.APPLICATION_JSON);
    
    HttpEntity<User> requestEntity = new HttpEntity<>(user, headers);
    
    // 使用execute方法发送请求
    User result = restTemplate.execute(
        "https://api.example.com/users/{id}",
        HttpMethod.PUT,
        // 请求回调
        req -> {
            // 准备请求阶段的操作
            return restTemplate.httpEntityCallback(requestEntity).doWithRequest(req);
        },
        // 响应提取器
        res -> {
            // 处理响应阶段的操作
            return restTemplate.responseEntityExtractor(User.class).extractData(res).getBody();
        },
        456  // URL变量
    );
    
    System.out.println("执行结果: " + result);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

execute方法通常用于以下场景:

  1. 需要在请求发送前进行特殊处理
  2. 需要自定义响应处理逻辑
  3. 实现RestTemplate没有直接提供的特殊HTTP方法

# 4. URL参数传递技巧

RestTemplate提供了两种传递URL参数的方式:

# 4.1 路径变量(Path Variables)

通过在URL中使用{占位符}并提供对应值:

/**
 * 使用路径变量传递参数
 */
public void pathVariablesDemo() {
    // 使用有序参数填充占位符
    String url = "https://api.example.com/users/{id}/posts/{postId}";
    User user = restTemplate.getForObject(url, User.class, 123, 456);
    
    // 占位符名称可以任意,与参数顺序对应即可
    String url2 = "https://api.example.com/users/{userId}/posts/{post}";
    User user2 = restTemplate.getForObject(url2, User.class, 123, 456);
    
    // 结果相同,都会访问 https://api.example.com/users/123/posts/456
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 4.2 查询参数(Query Parameters)

通过Map传递查询参数:

/**
 * 使用Map传递查询参数
 */
public void queryParamsDemo() {
    // 基础URL
    String baseUrl = "https://api.example.com/users";
    
    // 创建查询参数
    Map<String, String> params = new HashMap<>();
    params.put("name", "张三");
    params.put("age", "30");
    
    // 发送请求,参数会以key=value的形式附加到URL后面
    // 结果URL: https://api.example.com/users?name=张三&age=30
    User[] users = restTemplate.getForObject(baseUrl, User[].class, params);
    
    System.out.println("查询结果数量: " + users.length);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

此外,也可以直接在URL字符串中添加查询参数:

/**
 * 直接在URL中添加查询参数
 */
public void hardcodedQueryParamsDemo() {
    String url = "https://api.example.com/users?active=true&role=admin";
    User[] users = restTemplate.getForObject(url, User[].class);
}
1
2
3
4
5
6
7

# 5. 异常处理

RestTemplate在请求过程中可能遇到各种异常,需要进行适当处理:

/**
 * RestTemplate异常处理示例
 */
public void exceptionHandlingDemo() {
    try {
        // 尝试访问可能不存在的资源
        User user = restTemplate.getForObject(
            "https://api.example.com/users/{id}", 
            User.class, 
            99999
        );
        
        System.out.println("用户信息: " + user);
    } catch (HttpClientErrorException e) {
        // 处理客户端错误(4xx状态码)
        if (e.getStatusCode() == HttpStatus.NOT_FOUND) {
            System.out.println("用户不存在");
        } else if (e.getStatusCode() == HttpStatus.UNAUTHORIZED) {
            System.out.println("未授权访问");
        } else {
            System.out.println("客户端错误: " + e.getStatusCode());
        }
        System.out.println("错误响应内容: " + e.getResponseBodyAsString());
    } catch (HttpServerErrorException e) {
        // 处理服务器错误(5xx状态码)
        System.out.println("服务器错误: " + e.getStatusCode());
        System.out.println("错误响应内容: " + e.getResponseBodyAsString());
    } catch (ResourceAccessException e) {
        // 处理网络连接问题
        System.out.println("网络错误: " + e.getMessage());
    } catch (RestClientException e) {
        // 处理其他RestTemplate相关错误
        System.out.println("REST客户端错误: " + e.getMessage());
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

RestTemplate常见异常类型:

  • HttpClientErrorException: 4xx客户端错误
  • HttpServerErrorException: 5xx服务器错误
  • ResourceAccessException: 网络连接问题
  • RestClientException: 其他RestTemplate相关错误

# 6. 高级应用

# 6.1 使用ResponseErrorHandler处理错误

自定义错误处理器可以集中处理HTTP错误:

/**
 * 自定义RestTemplate错误处理器
 */
public class CustomResponseErrorHandler implements ResponseErrorHandler {
    @Override
    public boolean hasError(ClientHttpResponse response) throws IOException {
        // 判断响应是否包含错误
        return response.getStatusCode().is4xxClientError() || 
               response.getStatusCode().is5xxServerError();
    }

    @Override
    public void handleError(ClientHttpResponse response) throws IOException {
        // 处理错误响应
        if (response.getStatusCode().is4xxClientError()) {
            // 处理4xx错误
            if (response.getStatusCode() == HttpStatus.NOT_FOUND) {
                throw new ResourceNotFoundException("请求的资源不存在");
            } else if (response.getStatusCode() == HttpStatus.UNAUTHORIZED) {
                throw new UnauthorizedException("未授权访问");
            }
            // 处理其他4xx错误...
        } else if (response.getStatusCode().is5xxServerError()) {
            // 处理5xx错误
            throw new ServerErrorException("服务器错误: " + response.getStatusCode());
        }
    }
}

// 自定义异常类
class ResourceNotFoundException extends RuntimeException {
    public ResourceNotFoundException(String message) {
        super(message);
    }
}

class UnauthorizedException extends RuntimeException {
    public UnauthorizedException(String message) {
        super(message);
    }
}

class ServerErrorException extends RuntimeException {
    public ServerErrorException(String message) {
        super(message);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47

将自定义错误处理器应用到RestTemplate:

@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
    RestTemplate restTemplate = builder
            .setConnectTimeout(Duration.ofSeconds(5))
            .setReadTimeout(Duration.ofSeconds(5))
            .build();
    
    // 设置自定义错误处理器
    restTemplate.setErrorHandler(new CustomResponseErrorHandler());
    
    return restTemplate;
}
1
2
3
4
5
6
7
8
9
10
11
12

# 6.2 使用HttpMessageConverter自定义序列化和反序列化

可以定制RestTemplate如何转换请求体和响应体:

@Bean
public RestTemplate restTemplate() {
    RestTemplate restTemplate = new RestTemplate();
    
    // 获取当前的转换器列表
    List<HttpMessageConverter<?>> converters = restTemplate.getMessageConverters();
    
    // 移除默认的MappingJackson2HttpMessageConverter
    converters.removeIf(converter -> converter instanceof MappingJackson2HttpMessageConverter);
    
    // 添加自定义的Jackson转换器
    MappingJackson2HttpMessageConverter customConverter = new MappingJackson2HttpMessageConverter();
    ObjectMapper objectMapper = new ObjectMapper();
    
    // 配置ObjectMapper
    objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); // 忽略null值字段
    objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); // 忽略未知属性
    
    customConverter.setObjectMapper(objectMapper);
    converters.add(customConverter);
    
    return restTemplate;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# 6.3 配置连接池

默认情况下,RestTemplate每次请求都会创建新的连接。对于高并发场景,建议使用连接池:

@Bean
public RestTemplate restTemplate() {
    // 创建连接管理器
    PoolingHttpClientConnectionManager connectionManager = 
        new PoolingHttpClientConnectionManager();
    // 设置最大连接数
    connectionManager.setMaxTotal(100);
    // 设置每个路由的最大连接数
    connectionManager.setDefaultMaxPerRoute(20);
    
    // 创建HttpClient构建器
    HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
    httpClientBuilder.setConnectionManager(connectionManager);
    
    // 配置请求超时
    RequestConfig requestConfig = RequestConfig.custom()
            .setConnectTimeout(5000)  // 连接超时时间(毫秒)
            .setSocketTimeout(5000)   // 读取超时时间(毫秒)
            .build();
    httpClientBuilder.setDefaultRequestConfig(requestConfig);
    
    // 创建HttpClient
    CloseableHttpClient httpClient = httpClientBuilder.build();
    
    // 使用HttpComponentsClientHttpRequestFactory
    HttpComponentsClientHttpRequestFactory requestFactory = 
        new HttpComponentsClientHttpRequestFactory(httpClient);
    
    // 创建RestTemplate
    return new RestTemplate(requestFactory);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

参考资料

  • Spring官方文档: https://docs.spring.io/spring-framework/docs/current/reference/html/integration.html#rest-client-access (opens new window)
  • RestTemplate API: https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/client/RestTemplate.html (opens new window)
编辑此页 (opens new window)
上次更新: 2025/03/21, 11:11:36
Spring Boot - 参数校验
Spring Boot - 控制器请求映射

← Spring Boot - 参数校验 Spring Boot - 控制器请求映射→

Theme by Vdoing | Copyright © 2019-2025 程序员scholar
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式