使用 OpenFeign 发起 PATCH 请求
1、概览
通过 REST API 更新资源时,可以使用 PATCH
方法。该方法专门用于“更新部分字段”的场景。当需要完全更改现有资源时(全量替换),可以使用 PUT
方法。
在本教程中,我们将学习如何在 OpenFeign 中设置 HTTP PATCH 方法。我们还将演示在 Feign client 测试 PATCH
方法时出现的异常情况,以及解决方案。
2、Spring Boot 中的应用示例
假设我们需要构建一个简单的微服务,调用下游服务进行部分更新。
2.1、Maven 依赖
首先,我们要添加 spring-boot-starter-web 和 spring-cloud-starter-openfeign 依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
2.2、实现 Feign Client
现在,让我们使用 Spring Web 注解在 Feign 中实现 PATCH
方法。
首先,让我们建立一个 User
model,它有几个简单的属性。
public class User {
private String userId;
private String userName;
private String email;
}
接下来,我们将使用 updateUser
方法来实现一个 UserClient
接口:
@FeignClient(name = "user-client", url = "http://localhost:8082/api/user")
public interface UserClient {
@RequestMapping(value = "{userId}", method = RequestMethod.PATCH)
User updateUser(@PathVariable(value = "userId") String userId, @RequestBody User user);
}
在上述 PATCH
方法中,我们传递的 User
对象只包含需要更新的字段和 userId
字段。这样做比发送完整的资源对象更省事,节省了一些网络带宽,并避免了同一对象在不同字段上进行多次更新时的冲突。
相比之下,如果我们使用 PUT
请求,就必须传递完整的资源对象表示来替换现有资源。
3、测试 Feign Client
现在,让我们通过模拟 HTTP 调用为 UserClient
实现一个测试用例。
3.1、设置 WireMock Server
为了进行实验,我们需要使用模拟框架来模拟我们正在调用的服务。
首先,让我们加入 WireMockServer Maven 依赖:
<dependency>
<groupId>com.github.tomakehurst</groupId>
<artifactId>wiremock-jre8</artifactId>
<version>2.35.0</version>
<scope>test</scope>
</dependency>
然后,配置并启动 WireMockServer
:
WireMockServer wireMockServer = new WireMockServer(8082);
configureFor("localhost", 8082);
wireMockServer.start();
WireMockServer
使用的 host
和 port
与 Feign client 配置的相同。
3.2、PATCH API
我们将模拟 PATCH
方法来测试更新 User
的 API:
String updatedUserResponse = "{\n" +
"\"userId\": 100001,\n" +
"\"userName\": \"name\",\n" +
"\"email\": \"updated-email@mail.in\"\n" +
"}";
stubFor(patch(urlEqualTo("/api/user/".concat(USER_ID)))
.willReturn(aResponse().withStatus(HttpStatus.OK.value())
.withHeader("Content-Type", "application/json")
.withBody(updatedUserResponse)));
3.3、测试 PATCH 请求
测试时,我们将把 User
对象连同需要更新的字段一起传递给 UserClient
。
现在,让我们执行测试并验证更新功能:
User user = new User();
user.setUserId("100001");
user.setEmail("updated-email@mail.in");
User updatedUser = userClient.updateUser("100001", user);
assertEquals(user.getUserId(), updatedUser.getUserId());
assertEquals(user.getEmail(), updatedUser.getEmail());
上述测试代码看起来没有任何问题,但执行会抛出异常。
feign.RetryableException: Invalid HTTP method: PATCH executing PATCH http://localhost:8082/api/user/100001
at feign.FeignException.errorExecuting(FeignException.java:268)
at feign.SynchronousMethodHandler.executeAndDecode(SynchronousMethodHandler.java:131)
at feign.SynchronousMethodHandler.invoke(SynchronousMethodHandler.java:91)
at feign.ReflectiveFeign$FeignInvocationHandler.invoke(ReflectiveFeign.java:100)
at jdk.proxy2/jdk.proxy2.$Proxy80.updateUser(Unknown Source)
at com.baeldung.cloud.openfeign.patcherror.client.UserClientUnitTest.givenUserExistsAndIsValid_whenUpdateUserCalled_thenReturnSuccess(UserClientUnitTest.java:64)
...
接下来,让我们详细研究一下这个异常。
3.4、出现 Invalid HTTP Method 错误的原因
上述异常信息表明所请求的 HTTP 方法无效。不过,根据 HTTP 标准,PATCH 方法是有效的。
从异常堆栈中看到,异常类型是 ProtocolException
,由 HttpURLConnection
类抛出:
Caused by: java.net.ProtocolException: Invalid HTTP method: PATCH
at java.base/java.net.HttpURLConnection.setRequestMethod(HttpURLConnection.java:489)
at java.base/sun.net.www.protocol.http.HttpURLConnection.setRequestMethod(HttpURLConnection.java:598)
at feign.Client$Default.convertAndSend(Client.java:170)
at feign.Client$Default.execute(Client.java:104)
at feign.SynchronousMethodHandler.executeAndDecode(SynchronousMethodHandler.java:119)
原来,默认 HTTP 客户端使用 HttpURLConnection
类来建立 HTTP 连接。HttpURLConnection
有一个 setRequestMethod
方法,用于设置请求方法。
遗憾的是,HttpURLConnection
类不支持 PATCH
方法。
4、处理 PATCH Method 异常
为修复该错误,我们将添加一个受支持的 HTTP Client 依赖。另外,我们还需要通过配置属性来覆盖默认的 HTTP 客户端。
4.1、添加 OkHttpClient
依赖
添加 feign-okhttp 依赖:
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-okhttp</artifactId>
</dependency>
除了 Okhttp 外,任何其他受支持的 HTTP client(如 ApacheHttpClient
)也可以。
4.2、启用 OkHttpClient
OkHttpClient
类将 PATCH
视为有效的 HTTP 方法,不会抛出任何异常。
让我们使用下面的配置来启用 OkHttpClient
类:
feign.okhttp.enabled=true
最后,我们重新运行测试,验证 PATCH
方法是否有效。输出的日志如下,一切OK, Feign client 未抛出任何异常。
UserClientUnitTest.givenUserExistsAndIsValid_whenUpdateUserCalled_thenReturnSuccess: 1 total, 1 passed
5、总结
在本文中,我们学习了如何使用 OpenFeign
发起 PATCH
请求,以及如何使用 OkHttpClient
来处理 “Invalid HTTP method: PATCH” 异常。
参考:https://www.baeldung.com/openfeign-http-patch-request