RestTemplate 调用 PATCH 接口踩坑实录与完整解决方案

2026-01-11 6点热度 0人点赞 0条评论

在 Spring 项目里用 RestTemplate 调外部接口,本来是个很日常的需求,但一旦遇到 PATCH 方法,就很容易踩坑。
这篇笔记整理下从报错到解决的完整过程,方便自己和同事以后少掉坑。


一、问题场景与报错现象

业务需要调用外部接口:

  • URL 形如:https://example.com/api/v1/resource/{id}
  • HTTP 方法:PATCH
  • 请求体:JSON

代码大致是这样的:

ResponseEntity<String> response = restTemplate.exchange(
        url,
        HttpMethod.PATCH,
        requestEntity,
        String.class
);

结果一跑就报:

java.net.ProtocolException: Invalid HTTP method: PATCH

说明根本还没走到业务逻辑,底层 HTTP 客户端就把 PATCH 判定为非法方法了。


二、根本原因:默认 RestTemplate 实现不支持 PATCH

Spring 的 RestTemplate 只是一个客户端封装,真正发请求的是底层 ClientHttpRequestFactory
如果你什么都不配置,Spring 会默认使用:

  • SimpleClientHttpRequestFactory → 底层是 JDK 自带的 HttpURLConnection

而 HttpURLConnection 在部分 JDK 版本/实现里只支持以下方法:

  • GET
  • POST
  • HEAD
  • OPTIONS
  • PUT
  • DELETE
  • TRACE

不支持 PATCH,于是就抛出了 java.net.ProtocolException: Invalid HTTP method: PATCH

所以,问题根源不是 RestTemplate 本身,而是:
底层的 HTTP 实现选错了,换成支持 PATCH 的实现即可。


三、解决思路:切换到底层 Apache HttpClient(支持 PATCH)

思路很简单:

  • 继续用 RestTemplate,不改业务调用姿势;
  • 把底层的 ClientHttpRequestFactory 换成基于 Apache HttpClient 的实现;
  • 这样 HttpMethod.PATCH 就会被正确处理。

Spring 已经提供了对应的工厂实现:

  • HttpComponentsClientHttpRequestFactory
    它会使用 Apache HttpClient 作为底层,实现对更多 HTTP 方法的支持,包括 PATCH

四、具体实现步骤

1. 引入 Apache HttpClient 依赖(以 HttpClient5 为例)

pom.xml 中添加依赖(不暴露业务项目名称,仅给出最小依赖片段):

<!-- Apache HttpClient5: 让 RestTemplate 支持 PATCH 等方法 -->
<dependency>
    <groupId>org.apache.httpcomponents.client5</groupId>
    <artifactId>httpclient5</artifactId>
    <version>5.2.1</version>
</dependency>

版本号可以根据你项目实际情况调整,注意和 Spring Boot 版本的兼容性。


2. 配置 RestTemplate 使用 HttpComponentsClientHttpRequestFactory

新建或修改统一的 RestTemplate 配置类,例如:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.web.client.RestTemplate;

import java.nio.charset.StandardCharsets;

@Configuration
public class RestTemplateConfig {

    @Bean
    public RestTemplate restTemplate(ClientHttpRequestFactory factory) {
        RestTemplate restTemplate = new RestTemplate(factory);
        // 支持 UTF-8 文本(特别是中文)
        restTemplate.getMessageConverters()
                .set(1, new StringHttpMessageConverter(StandardCharsets.UTF_8));
        return restTemplate;
    }

    @Bean
    public ClientHttpRequestFactory clientHttpRequestFactory() {
        // 使用 HttpComponentsClientHttpRequestFactory(底层 Apache HttpClient 支持 PATCH)
        HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
        factory.setConnectTimeout(15000);
        factory.setReadTimeout(30000);
        // bufferRequestBody 默认就是 true,一般不需要改动
        return factory;
    }
}

关键点:

  • RestTemplate 的 Bean 仍然通过注入 ClientHttpRequestFactory 创建;
  • ClientHttpRequestFactory 返回 HttpComponentsClientHttpRequestFactory
  • 超时可按项目实际做统一设置;
  • 默认 bufferRequestBody = true,对于大部分 JSON 请求体场景都 OK。

五、业务调用示例:JSON + PATCH

有了上面的配置之后,就可以在任何地方注入 RestTemplate 并愉快地使用 PATCH

1. 构建 JSON 请求体

import org.json.JSONObject;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;

public class SomeService {

    private final RestTemplate restTemplate;

    public SomeService(RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
    }

    public String updateResource(Integer id, String name) {
        String url = "https://example.com/api/v1/resources/" + id;

        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        headers.setBearerAuth("your-access-token");

        JSONObject jsonBody = new JSONObject();
        jsonBody.put("name", name);
        jsonBody.put("description", "some description");

        HttpEntity<String> requestEntity =
                new HttpEntity<>(jsonBody.toString(), headers);

        ResponseEntity<String> response = restTemplate.exchange(
                url,
                HttpMethod.PATCH,
                requestEntity,
                String.class
        );

        return response.getBody();
    }
}

只要底层工厂已切到 HttpComponentsClientHttpRequestFactory,这段代码就不会再抛 Invalid HTTP method: PATCH


六、几个容易忽略的小点

  • 必须重启应用
    配置类和依赖修改之后,需要重新启动应用让新的 Bean 配置生效。
  • 确认没有多个 RestTemplate Bean 冲突
    如果项目里有多个 RestTemplate 定义,要确认业务使用的是你配置好的那个。
  • 请求体大小与 bufferRequestBody
    默认是启用缓冲(bufferRequestBody = true),适合大多数 JSON 接口;
    若改为 false 会走 Streaming 实现,部分调用方式(尤其是自定义 RequestCallback 时调用 getBody())会抛出 UnsupportedOperationException,一般不建议轻易关闭。

七、小结

整个问题的来龙去脉可以概括为三句话:

  1. RestTemplate 默认基于 HttpURLConnection,不支持 PATCH,导致 Invalid HTTP method: PATCH
  2. 解决办法是替换底层实现为 Apache HttpClient,对应 Spring 的 HttpComponentsClientHttpRequestFactory
  3. 配好 pom.xml 依赖 + RestTemplateConfig 之后,直接使用 HttpMethod.PATCH 加 JSON 请求体即可。

以后再遇到 RestTemplate + PATCH 报 ProtocolException,就可以直接按这套方案检查:

  • 是否引入 HttpClient 依赖?
  • RestTemplate 是否使用 HttpComponentsClientHttpRequestFactory
  • 应用是否已经重启让配置生效?

admin

这个人很懒,什么都没留下

文章评论

您需要 登录 之后才可以评论