RestTemplate全解析

本文详细介绍了如何配置和使用Spring的RestTemplate,包括初始化配置、设置超时时间、支持HTTPS、添加拦截器以及发送POST请求的方法。通过示例代码展示了发送JSON和FORM表单格式的POST请求,并解析了RestTemplate内部的工作原理,强调了数据序列化的重要性。文章旨在帮助开发者避免使用RestTemplate时的常见误区,提供了一套完整的解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

RestTemplate 使用全解

[TOC]

RestTemplate 非常难用,代码风格不统一,设置繁琐,API 文档不够详细,导致 在使用的时候,非常容易陷入误区。
本人秉承完全免费共享的态度,将其总结为以下几点。希望 众人莫再入坑。 愿这世界 没有墙,自由呼吸民主气息。

1. 初始化配置

@Configuration
@Slf4j(topic = "REST客户端")
public class RestTemplateConfig {

    @SneakyThrows
    @Bean
    public RestTemplate restTemplate() {
        RestTemplate template = new RestTemplate(new BufferingClientHttpRequestFactory(factory())); // 设置http配置工厂 
        template.setInterceptors(restInterceptor()); // 拦截器 
        return template;
    }
}

2. 工厂类设置超时时间

@SneakyThrows
private ClientHttpRequestFactory factory() {

    HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
    requestFactory.setHttpClient(httpClient()); // 支持 https 

    // 从pool池中获取可用连接的时间,1 s
    requestFactory.setConnectionRequestTimeout(1 * 1000);
    // 与远程服务器建立一个新连接所需要的时间, 10 s
    requestFactory.setConnectTimeout(55 * 1000);
    // 数据包开始传输之前的等待时间, 60 s
    requestFactory.setReadTimeout(55 * 1000);
    return requestFactory;
}

3. set httpclient客户端支持ssl ? 支持本地测试调用 https

/**
 * support https on local test
 * 不明觉厉,这是经过测验的唯一可用 支持 https 不抛错的代码。如您有更好的思路,请在评论区或者私信分享。
 */
@SneakyThrows
private CloseableHttpClient httpClient() {
    TrustStrategy acceptingTrustStrategy = new TrustStrategy() {
        @Override
        public boolean isTrusted(X509Certificate[] chain, String authType) {
            return true;
        }
    };

    SSLContext sslContext = org.apache.http.ssl.SSLContexts.custom()
        .loadTrustMaterial(null, acceptingTrustStrategy)
        .build();

    SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext);

    return HttpClients.custom()
        .setSSLSocketFactory(csf)
        .setRedirectStrategy(new LaxRedirectStrategy())
        .build();
}

4. 设置拦截器,记录每次请求时长 。

modify request header & add response header

/**
 * 设置 拦截器
 */
@Bean
public List<ClientHttpRequestInterceptor> restInterceptor() {
    List<ClientHttpRequestInterceptor> interceptors = new ArrayList<>();

    // header 1 : foo:bar 
    ClientHttpRequestInterceptor header = new ClientHttpRequestInterceptor() {
        @Override
        public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
            ClientHttpResponse response = execution.execute(request, body);
            response.getHeaders().add("Foo", "bar");
            return response;
        }
    };

    // header 2: cost-milliseconds:time
    ClientHttpRequestInterceptor timer = new ClientHttpRequestInterceptor() {
        @Override
        public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
            StopWatch watch = new StopWatch();
            watch.start();

            ClientHttpResponse response = execution.execute(request, body);
            watch.stop();

            String time = String.valueOf(watch.getTime(TimeUnit.MILLISECONDS));
            int rawStatusCode = response.getRawStatusCode();
            String statusText = response.getStatusText();
            response.getHeaders().add("cost-milliseconds", time);
            log.info("发送rest请求耗时 :{} ms,response:{} {},{} {} headers:{}", time,rawStatusCode,statusText, request.getMethod(), request.getURI(), request.getHeaders());
            return response;
        }
    };

    interceptors.add(timer);
    interceptors.add(header);

    return interceptors;
}

5. 如何发送请求

5.1 如何发送 post 请求,json 格式数据

/**
  * post 发送json 请求
  * @param json Map
  * @see RestTemplate#RestTemplate()
  * @see org.springframework.web.client.RestTemplate.HttpEntityRequestCallback#doWithRequest(org.springframework.http.client.ClientHttpRequest)
  */
@SneakyThrows
public static ResponseEntity<String> postJson(String postUrl, Map<String, Object> json) {

    HttpHeaders headers = new HttpHeaders();
    headers.setContentType(MediaType.APPLICATION_JSON);

    // HttpEntity request = new HttpEntity<>(JsonUtils.writeValueAsString(json), headers);
    HttpEntity request = new HttpEntity<>(json, headers);
    ResponseEntity<String> responseEntityStr = staticRest.postForEntity(postUrl, request, String.class);
    return responseEntityStr;
}

/**
 * 方法重载,直接传输 序列化好之后的 json String 也是可以的
 * @param jsonStr 必须是符合json 序列化标准的 字符串 ,即: jsonStr = new ObjectMapper().writeValueAsString(json);
 */
public static ResponseEntity<String> postJson(String postUrl,String jsonStr) {

    HttpHeaders headers = new HttpHeaders();
    headers.setContentType(MediaType.APPLICATION_JSON);

    HttpEntity request = new HttpEntity<>(jsonStr, headers);
    ResponseEntity<String> responseEntityStr = staticRest.postForEntity(postUrl, request, String.class);
    return responseEntityStr;
}

5.2 如何发送 post 请求,form表单格式

/**
 * post 发送 form 表单类型的请求
 * @param postMap 这个map的value 都是 list [] 格式的,专门为了 form 表单的多name而生 。
 * 并且该map中的value,必须都是 string类型的,否则会抱错 类型转换错误
 *      final long t = System.currentTimeMillis();
 *       MultiValueMap<String, Object> postMap = new LinkedMultiValueMap() {{
 *           add(BanConstants.forbiddenIds, JsonUtils.writeValueAsString(onceList));
 *           add(BanConstants.gameId, aId);
 *           add(BanConstants.t, "" + t);
 *       }};
 */
@SneakyThrows
public static ResponseEntity postForm(String url, MultiValueMap postMap) {

    RequestEntity requestEntity = RequestEntity
        .post(new URI(""))
        .contentType(MediaType.APPLICATION_FORM_URLENCODED)
        .accept(MediaType.ALL).acceptCharset(Charset.defaultCharset())
        .body(postMap);

    ResponseEntity<String> result = staticRest.postForEntity(url, requestEntity, String.class);

    return result;
}

6. 原理说明

随便贴上几张源码截图,对 步骤 # 5.1 中的注释部分进行一个 说明

网络传输,最终的格式必须是序列化为字符串或者2进制格式的。
因此,无论 RestTemplate 传输的 json 格式,还是 form格式,其实最终传输的都是字符串。

  • rest 在无参构造初始化时,会wrap一系列的 序列化转换器,如图

    image-20210729203315566

​ 而我们的初始化1 ,第一步是设置了 factory ,重载的有参构造源码如下

this(); // 回调无参构造,设置所有可能的 消息转换器 。

image-20210729203525732

  • rest 在传输 json格式之前,是必须要进行序列化的,代码如下 :

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Mie0pYA9-1627563241961)(https://raw.githubusercontent.com/wshidaijingshen/iHyCU2mXpYNpqG-u8dKcjvOmD/master/img/image-20210729204435537.png)]

  • 所以,无论我们采用何种方式,发送数据,最终都会被序列化为字符串或者2进制。
    所不同的是,json格式序列化,采用的肯定是符合json规范的方式,比如 调用 new ObjectMapper().writeValueAsString(json) .这样,我们客户端发出去的这个json,才能被接收方 服务器正确把字符串解析出来,进而再进行后续处理。

  • 而,当我们传输的是 form格式,或者 get请求时,参数就肯定是 字符串了,无需预处理,直接发送即可,再大不了就是个 url的编解码过程。

  • 因此, 别看 是 json格式,其实 背后还是字符串,所以我们也可以直接传输 序列化好之后的 json字符串,但是必须满足格式才行喔!因为传输的双方已经约定好了 contentType = json; 不按套路来,那可是要不得滴~

<think>首先,用户的问题是:“我想解析RestTemplate各个参数的作用和使用方法 请问RestTemplate 参数详解 使用方法”。这似乎是关于Java的RestTemplate类的参数解析和使用方法。 根据系统级指令,我需要: - 使用中文回答。 - 结构清晰,帮助用户逐步解决问题。 - 保证回答真实可靠。 - 在行内数学表达式中使用$...$,但这里可能不涉及数学表达式,所以可能不需要。 - 独立公式使用$$...$$,但同样,可能不适用。 - 生成相关问题。 - 在引用的段落末尾添加引用标识,如[^1]。 用户提供了两个引用: - 引用[1]:关于使用RestTemplate提交表单参数。 - 引用[2]:关于RestTemplate带参数请求Post接口的注意事项。 这些引用似乎是用户提供的上下文,我需要参考它们,但用户说“上述引用仅供参考,并不是用户真正的需求”,所以我应该基于用户的问题来回答,而不是直接依赖这些引用。 用户的核心问题是解析RestTemplate的各个参数,包括作用和使用方法。RestTemplate是Spring框架中的一个类,用于发送HTTP请求。 关键点: - RestTemplate的参数:这可能指的是在方法调用中使用的参数,如postForEntity、getForObject等方法的参数。 - 我需要详解这些参数:作用、类型、用法。 回顾RestTemplate的常见方法: - getForObject(url, responseType, uriVariables) - postForObject(url, request, responseType, uriVariables) - postForEntity(url, request, responseType, uriVariables) - 等等。 参数详解: - url:请求的URL。 - request:请求体,可以是对象、HttpEntity等。 - responseType:期望的响应类型。 - uriVariables:URL中的变量,用于路径参数。 在用户提供的引用中: - 引用[1]展示了使用HttpEntity封装MultiValueMap和Headers。 - 引用[2]展示了类似的使用,并提到了一些注意事项。 所以,我应该基于此构建回答。 回答结构: 1. **介绍RestTemplate**:简要解释RestTemplate是什么。 2. **详解参数**:针对常用方法,如postForEntity,解析各个参数。 3. **使用示例**:提供代码示例,基于用户引用。 4. **注意事项**:包括用户引用中提到的坑。 5. **生成相关问题**:在最后添加相关问题。 确保回答真实可靠:我熟悉Java和Spring,所以可以基于知识回答。 最终输出格式: - 使用中文。 - 清晰的结构:分步解释。 - 在适当的地方添加引用标识。用户提供了引用[1]和[2],所以我可以在讨论相关点时引用它们。 - 生成相关问题。 相关问题生成:基于主题,如优化、常见错误、替代方案等。</think>### RestTemplate 参数详解与使用方法 RestTemplate 是 Spring Framework 中用于简化 HTTP 请求的类,它支持 GET、POST 等 HTTP 方法,常用于 RESTful API 调用。下面我将逐步解析 RestTemplate 的核心参数、作用和使用方法,确保内容真实可靠(基于 Spring 官方文档和常见实践)。注意,RestTemplate 的参数主要集中在方法调用中,如 `postForEntity()`、`getForObject()` 等。我会以常用方法 `postForEntity()` 为例进行详解,因为它涵盖了大多数参数场景。 #### 1. **RestTemplate 核心参数详解** RestTemplate 的方法参数通常包括 URL、请求体、响应类型和 URI 变量。以下是关键参数的详细说明: - **`url` (String 类型)**: - **作用**:指定目标 API 的 URL。可以是完整路径或包含占位符的模板(例如 `http://example.com/{id}`)。 - **使用方法**:直接传入字符串。如果 URL 包含路径变量(如 `{id}`),需配合 `uriVariables` 参数填充。 - **示例**:`restTemplate.postForEntity("http://localhost:8080/api", request, String.class)` - **`request` (Object 类型)**: - **作用**:封装 HTTP 请求体(body)和请求头(headers)。通常使用 `HttpEntity` 对象来包装,支持多种数据类型(如表单数据、JSON、二进制文件)。 - **使用方法**: - 对于表单提交:使用 `MultiValueMap` 存储键值对,并设置 `Content-Type` 为 `MediaType.APPLICATION_FORM_URLENCODED`,如引用[1]所示。这确保参数以 `x-www-form-urlencoded` 格式发送。 - 对于 JSON 或其他类型:可直接传入 POJO 对象,或自定义 `HttpEntity` 设置 headers。 - **注意事项**:如果忘记设置 `Content-Type`,可能导致服务器无法解析参数,如引用[2]提到的“大坑”。例如,发送二进制文件时,需显式设置 headers[^2]。 - **`responseType` (Class<T> 类型)**: - **作用**:指定响应体的反序列化类型。RestTemplate 会自动将 HTTP 响应转换为指定类的对象。 - **使用方法**:传入 Java 类(如 `String.class`、`ResponseDTO.class`)。如果响应是 JSON,Spring 会使用 `HttpMessageConverter` 进行转换。 - **示例**:`ResponseEntity<ResponseDTO> response = restTemplate.postForEntity(url, request, ResponseDTO.class)` - **`uriVariables` (Object... 或 Map<String, ?> 类型)**: - **作用**:用于填充 URL 中的路径变量(占位符)。例如,URL `http://example.com/user/{id}` 中的 `{id}` 可通过此参数替换。 - **使用方法**: - 可变参数形式:`restTemplate.getForObject("http://example.com/{id}", String.class, 123)`,其中 `123` 替换 `{id}`。 - Map 形式:`Map<String, Object> vars = new HashMap<>(); vars.put("id", 123);` - **注意事项**:如果 URL 无占位符,此参数可省略。确保变量名与 URL 中的占位符一致,否则会抛出异常。 #### 2. **完整使用示例** 以下是一个基于引用[1]和[2]的完整示例,展示如何用 RestTemplate 提交表单参数(如登录场景)。代码中标注了关键参数的作用: ```java import org.springframework.http.*; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import org.springframework.web.client.RestTemplate; public class RestTemplateDemo { public static void main(String[] args) { // 创建 RestTemplate 实例 RestTemplate restTemplate = new RestTemplate(); // 步骤1: 准备请求参数 (request 参数) // 使用 MultiValueMap 存储表单数据,支持多值参数(如复选框) MultiValueMap<String, String> formData = new LinkedMultiValueMap<>(); formData.add("username", "admin"); // 添加表单字段 formData.add("password", "password123"); // 步骤2: 设置请求头 (封装到 HttpEntity) HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); // 关键:设置 Content-Type HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(formData, headers); // 步骤3: 发送 POST 请求 (使用 postForEntity 方法) // 参数详解: // url = "http://localhost:8888/login" (目标 API) // request = requestEntity (封装请求体和头) // responseType = String.class (期望响应为字符串) ResponseEntity<String> response = restTemplate.postForEntity( "http://localhost:8888/login", // url 参数 requestEntity, // request 参数 String.class // responseType 参数 ); // 处理响应 System.out.println("响应状态码: " + response.getStatusCode()); System.out.println("响应体: " + response.getBody()); } } ``` **关键点说明**: - **参数组合**:`postForEntity()` 方法的核心参数是 `url`、`request` 和 `responseType`。`uriVariables` 未使用,因为 URL 无占位符。 - **为什么用 HttpEntity**:它统一封装请求体和 headers,避免常见错误。如引用[1]所示,正确设置 `Content-Type` 能确保服务器解析表单参数[^1]。 - **错误避免**:引用[2]强调,发送二进制数据(如文件)时,必须设置 headers 的 `Content-Type`,否则会导致序列化失败[^2]。例如: ```java // 发送二进制文件的片段(基于引用[2]) MultiValueMap<String, Object> paramMap = new LinkedMultiValueMap<>(); paramMap.add("bytes", "hello world".getBytes()); paramMap.add("photoName", "test.jpg"); HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.MULTIPART_FORM_DATA); // 必须设置 HttpEntity<MultiValueMap<String, Object>> entity = new HttpEntity<>(paramMap, headers); restTemplate.postForEntity("http://localhost:8882/upload", entity, String.class); ``` #### 3. **常见方法参数对比** RestTemplate 的其他常用方法参数类似,以下是简要对比: | **方法名** | **参数说明** | **使用场景** | |--------------------|-----------------------------------------------------------------------------|--------------------------------| | `getForObject()` | `url`, `responseType`, `uriVariables` (可选) | 发送 GET 请求,返回反序列化对象 | | `postForObject()` | `url`, `request`, `responseType`, `uriVariables` (可选) | 发送 POST 请求,返回反序列化对象 | | `exchange()` | `url`, `httpMethod`, `requestEntity`, `responseType`, `uriVariables` (可选) | 灵活支持所有 HTTP 方法(如 PUT、DELETE) | #### 4. **注意事项与最佳实践** - **线程安**:RestTemplate 实例是线程安的,建议作为单例复用。 - **参数编码**:URL 参数需正确编码,避免特殊字符问题。使用 `UriComponentsBuilder` 构建 URL。 - **错误处理**:检查响应状态码(如 `response.getStatusCode() == HttpStatus.OK`),并处理异常(如 `RestClientException`)。 - **替代方案**:Spring 5 推荐使用 WebClient(响应式编程),但 RestTemplate 仍广泛用于同步请求。 - **性能优化**:为 RestTemplate 配置连接池(如 Apache HttpClient),减少重复创建开销。 通过以上解析,您应该能清晰理解 RestTemplate 的参数作用和使用方法。如果有特定场景(如文件上传或 OAuth 认证),可进一步调整参数。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值