1. 概述
在通过REST API更新对象时,使用PATCH方法是一种好实践。这样我们可以仅针对我们希望更改的字段进行部分更新。如果需要完全替换现有资源,也可以使用PUT方法。
本教程将指导如何在OpenFeign中设置HTTP PATCH方法。我们将探讨在Feign客户端测试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客户端
现在,让我们使用Spring的Web注解在Feign中实现PATCH方法。
首先,定义一个包含一些属性的User
类:
public class User {
private String userId;
private String userName;
private String email;
}
接下来,我们实现UserClient
接口及其updateUser
方法:
@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客户端的测试实现
现在,我们来为UserClient
实现一个测试用例,通过模拟HTTP调用来进行。
3.1. 设置WireMock服务器
为了实验,我们需要使用一个模拟框架来模拟我们正在调用的服务。
首先,添加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
将以Feign客户端配置的主机和端口启动。
3.2. 模拟PATCH API
我们将模拟PATCH方法来测试更新User
API:
String updatedUserResponse = "{\n" +
"\"userId\": 100001,\n" +
"\"userName\": \"name\",\n" +
"\"email\": \"[email protected]\"\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("[email protected]");
User updatedUser = userClient.updateUser("100001", user);
assertEquals(user.getUserId(), updatedUser.getUserId());
assertEquals(user.getEmail(), updatedUser.getEmail());
预期这个测试应该通过。然而,我们会收到Feign客户端的一个意外错误:
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. HTTP方法无效错误的原因
上述错误信息表明请求的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方法错误
要解决这个问题,我们需要添加一个支持的HTTP客户端依赖项。同时,我们将通过添加配置文件来覆盖默认的HTTP客户端。
4.1. 添加OkHttpClient
依赖
请添加feign-okhttp
依赖:
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-okhttp</artifactId>
</dependency>
我们需要注意,其他任何支持的HTTP客户端,如ApacheHttpClient
,也同样适用。
4.2. 启用OkHttpClient
OkHttpClient
类认为PATCH方法是有效的类型,不会抛出异常。
让我们通过以下配置启用OkHttpClient
:
feign.okhttp.enabled=true
最后,重新运行测试并验证PATCH方法是否工作。现在,Feign客户端不再抛出任何错误:
UserClientUnitTest.givenUserExistsAndIsValid_whenUpdateUserCalled_thenReturnSuccess: 1 total, 1 passed
5. 总结
在这篇文章中,我们学习了如何在OpenFeign中使用PATCH方法。我们也看到了测试时的一个意外错误,并理解了其根本原因,并进行了修复。
我们还通过OkHttpClient
的实现解决了问题。
如往常一样,示例代码可在GitHub上找到。