51工具盒子

依楼听风雨
笑看云卷云舒,淡观潮起潮落

使用 OpenFeign 发起 PATCH 请求

1、概览 {#1概览}

通过 REST API 更新资源时,可以使用 PATCH 方法。该方法专门用于"更新部分字段"的场景。当需要完全更改现有资源时(全量替换),可以使用 PUT 方法。

在本教程中,我们将学习如何在 OpenFeign 中设置 HTTP PATCH 方法。我们还将演示在 Feign client 测试 PATCH 方法时出现的异常情况,以及解决方案。

2、Spring Boot 中的应用示例 {#2spring-boot-中的应用示例}

假设我们需要构建一个简单的微服务,调用下游服务进行部分更新。

2.1、Maven 依赖 {#21maven-依赖}

首先,我们要添加 spring-boot-starter-webspring-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 {#22实现-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 {#3测试-feign-client}

现在,让我们通过模拟 HTTP 调用为 UserClient 实现一个测试用例。

3.1、设置 WireMock Server {#31设置-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 使用的 hostport 与 Feign client 配置的相同。

3.2、PATCH API {#32patch-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 请求 {#33测试-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 错误的原因 {#34出现-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 异常 {#4处理-patch-method-异常}

为修复该错误,我们将添加一个受支持的 HTTP Client 依赖。另外,我们还需要通过配置属性来覆盖默认的 HTTP 客户端。

4.1、添加 OkHttpClient 依赖 {#41添加-okhttpclient-依赖}

添加 feign-okhttp 依赖:

<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-okhttp</artifactId>
</dependency>

除了 Okhttp 外,任何其他受支持的 HTTP client(如 ApacheHttpClient)也可以。

4.2、启用 OkHttpClient {#42启用-okhttpclient}

OkHttpClient 类将 PATCH 视为有效的 HTTP 方法,不会抛出任何异常。

让我们使用下面的配置来启用 OkHttpClient 类:

feign.okhttp.enabled=true

最后,我们重新运行测试,验证 PATCH 方法是否有效。输出的日志如下,一切OK, Feign client 未抛出任何异常。

UserClientUnitTest.givenUserExistsAndIsValid_whenUpdateUserCalled_thenReturnSuccess: 1 total, 1 passed

5、总结 {#5总结}

在本文中,我们学习了如何使用 OpenFeign 发起 PATCH 请求,以及如何使用 OkHttpClient 来处理 "Invalid HTTP method: PATCH" 异常。


参考:https://www.baeldung.com/openfeign-http-patch-request

赞(0)
未经允许不得转载:工具盒子 » 使用 OpenFeign 发起 PATCH 请求