1. 概述
當通過 REST API 更新對象時,使用 PATCH 方法是一種最佳實踐。這允許我們使用要修改的字段進行部分更新。我們也可以使用 PUT 方法,當現有資源需要完全更改時。
在本教程中,我們將學習如何在 OpenFeign 中設置 HTTP PATCH 方法。我們還將遇到在測試 Feign 客户端的 PATCH 方法時出現意外錯誤。
最後,我們將瞭解根本原因並解決問題。
2. Spring Boot 示例應用程序
讓我們假設我們需要構建一個簡單的微服務,該微服務會調用下游服務以執行部分更新。
2.1. Maven 依賴
首先,我們將添加以下 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 客户端的測試
現在,我們將通過模擬 HTTP 調用來為 UserClient 實現一個測試用例。
3.1. 設置 WireMock 服務器
為了進行實驗,我們需要使用一個 Mocking 框架來模擬我們所調用的服務。
首先,讓我們包含 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 客户端所使用的相同 host 和 port 上啓動。
3.2. 模擬 PATCH API
我們將模擬 PATCH 方法,以測試更新 用户 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 passed5. 結論
在本文中,我們學習瞭如何使用 OpenFeign 中的 PATCH 方法。我們還發現了一個意外錯誤,並瞭解了其根本原因。
我們還使用 OkHttpClient 實現解決了該問題。